3

先列题目,然后加答案。

JS

1. Eventloop

主要是分三部分:主线程、宏任务队列(macrotask)、微任务队列(microtask)
主线程,就是访问到的script标签里面包含的内容,或者是直接访问某一个js文件的时候,里面的可以在当前作用域直接执行的所有内容(执行的方法,new出来的对象等)
宏队列(macrotask),setTimeout、setInterval、setImmediate、UI rendering等
微队列(microtask),promise.then、mutaionObserver、process.nextTick等
执行顺序
先执行主线程 ,遇到宏任务放到宏队列(macrotask),遇到微任务放到微队列(microtask。主线程执行完毕,调用栈被清空,这个时候就会从微队列里取出位于首位的回调放入执行栈开始执行,微队列长度-1,然后依次执行队列里的回调任务直到所有任务被执行完毕,此时微队列为空,调用栈也为空,这时再从宏队列里取出位于首位的一个任务,然后放入调用栈执行,执行完毕之后,再去取微队列里的任务,按照之前的步骤循环。

2. js为什么要实现成单线程的,有什么好处?

原因:作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。
单线程的好处:设想,如果 Javascript 被设计为多线程的程序,那么操作 DOM 必然会涉及到资源的竞争,那么这门语言必然会被实现的非常臃肿。在客户端中跑这么一门语言的程序,资源消耗和性能都将是不乐观的,同时在客户端实现多线程不是刚需。
设计成单线程,并辅以完善的异步队列来实现,那么运行成本就会比多线程的设计要小很多了。

3. 进程线程的区别

  1. 进程是资源分配的最小单位,线程是程序执行的最小单位(资源调度的最小单位)
  2. 进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。
    而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
  3. 线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。
  4. 但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。

4. 闭包的含义、原理、优缺点、使用场景、如何回收

含义:闭包是函数和声明该函数的词法环境的组合。这个环境包含了这个闭包创建时所能访问的所有局部变量。
闭包就是一个函数,这个函数能够访问其他函数的作用域中的变量,是函数内部和外部之间的桥梁。
原理:函数的作用域是一条作用域链,而且作用域链是有顺序的,我们称之为链式作用域结构。在js中,外部不能读取到内部的变量,而内部可以读取到外部的变量。利用这个特性,闭包可以将读取到的属性扔到外部使用。
优点:可以读取函数内部的变量,让这些变量的值始终保持在内存中。
缺点:闭包对脚本性能有负影响,不能滥用。并且闭包会在父函数外部,改变父函数内部变量的值。
使用场景:

  1. 给对象设置私有变量并且利用特权方法去访问私有属性
  2. 采用函数引用方式的setTimeout调用,原生的setTimeout传递的第一个函数不能带参数,通过闭包可以实现传参效果
  3. 单例模式的实现,确保全局只有一个实例对象
  4. module仿模块化,使用闭包封装“私有”状态和组织。只用返回一个公有的API,其他的所有规则都在私有闭包里,防止泄露到全局作用域,并且可以减少与别的开发人员的接口发生冲突。

5. JS数据类型有哪些,怎么做类型判断?

基本类型:string、number、boolean、null、undefined、symbol、bigInt
复杂类型:Object

普通基本类型:undefined、null、symbol
特殊基本包装类型:string、number、boolean

引用类型:Object、Data、function、Array

判断方法:

  1. typeof
  2. instanceof
  3. Object.prototype.toString.call(data)

6. 类型转换有哪些?

  1. 转string: toString(),String(),拼接空字符创
  2. 转Boolean: Boolean(),!(),!!()
  3. 转数字:Number(),parseInt(),+ data

7. 执行上下文

执行上下文是评估和执行 JavaScript 代码的环境的抽象概念。每当 Javascript 代码在运行的时候,它都是在执行上下文中运行。
JavaScript 中有三种执行上下文类型:
全局执行上下文 — 这是默认或者说基础的上下文,任何不在函数内部的代码都在全局上下文中。它会执行两件事:创建一个全局的 window 对象(浏览器的情况下),并且设置 this 的值等于这个全局对象。一个程序中只会有一个全局执行上下文。
函数执行上下文 — 每当一个函数被调用时, 都会为该函数创建一个新的上下文。每个函数都有它自己的执行上下文,不过是在函数被调用时创建的。函数上下文可以有任意多个。每当一个新的执行上下文被创建,它会按定义的顺序执行一系列步骤。
Eval 函数执行上下文 — 执行在 eval 函数内部的代码也会有它属于自己的执行上下文。

创建执行上下文分为两个阶段:1.创建阶段  2.执行阶段

在创建阶段会发生三件事:

  1. this 值的决定,即我们所熟知的 This 绑定。
  2. 创建词法环境组件。
  3. 创建变量环境组件。

8. new操作符做了什么

  1. 创建一个空对象 var o=new Object();
  2. 将空对象的原型赋值为构造函数A的原型 o.__proto__=A.prototype;
  3. 执行构造函数中的代码,为空对象添加属性A.call(o);,也可以理解为将构造器函数内部this指向新建的空对象。
  4. 返回添加属性后的对象。
function New(obj){ 
    var o = new Object(), 
        Constructor = obj; 
    o.__proto__ = Constructor.prototype; 
    Constructor.call(o); 
    return o; 
}

9. 解释构造函数、对象、原型链之间的关系

每个构造函数都有一个原型对象,原型对象上包含着一个指向构造函数的指针,而实例都包含着一个指向原型对象的内部指针。通俗的说,实例可以通过内部指针访问到原型对象,原型对象可以通过constructor找到构造函数。

10. 继承的几种方式以及优缺点

  1. 原型链继承
    缺点:引用类型的属性被所有实例共享;在创建 Child 的实例时,不能向Parent传参

    function Parent () {
        this.names = ['11', '22'];
    }
    function Child () {}
    Child.prototype = new Parent();
    var child1 = new Child();
    child1.names.push('33');
    console.log(child1.names); // ["11", "22", "33"]
    var child2 = new Child();
    console.log(child2.names); // ["11", "22", "33"]
  2. 构造函数继承
    优点:避免了引用类型的属性被所有实例共享,并且可以在 Child 中向 Parent 传参
    缺点:方法都在构造函数中定义,每次创建实例都会创建一遍方法。

    function Parent (name) {
        this.name = name;
    }
    function Child (name) {
        Parent.call(this, name);
    }
    var child1 = new Child('11');
    console.log(child1.name); // 11
    var child2 = new Child('22');
    console.log(child2.name); // 22
  3. 组合继承
    优点:融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式
    缺点:调用两次父级的构造函数

    function Parent (name) {
        this.name = name;
        this.colors = ['red', 'blue', 'green'];
    }
    Parent.prototype.getName = function () {
        console.log(this.name)
    }
    function Child (name, age) {
        Parent.call(this, name);
        this.age = age;
    }
    
    Child.prototype = new Parent();
    var child1 = new Child('kevin', '18');
    child1.colors.push('black');
    console.log(child1.name); // kevin
    console.log(child1.age); // 18
    console.log(child1.colors); // ["red", "blue", "green", "black"]
    
    var child2 = new Child('daisy', '20');
    console.log(child2.name); // daisy
    console.log(child2.age); // 20
    console.log(child2.colors); // ["red", "blue", "green"]
  4. 原型式继承

    function createObj(o) {
        function F(){}
        F.prototype = o;
        return new F();
    }

    缺点:包含引用类型的属性值始终都会共享相应的值,这点跟原型链继承一样。

  5. 寄生继承
    缺点:跟借用构造函数模式一样,每次创建对象都会创建一遍方法

    function createObj (o) {
        var clone = object.create(o);
        clone.sayName = function () {
            console.log('hi');
        }
        return clone;
    }
  6. 寄生组合式继承
    优点:这种方式的高效率体现它只调用了一次 Parent 构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof 和 isPrototypeOf。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。

    function object(o) {
        function F() {}
        F.prototype = o;
        return new F();
    }
    
    function prototype(child, parent) {
        var prototype = object(parent.prototype);
        prototype.constructor = child;
        child.prototype = prototype;
    }

11. 实现原型式继承

function createObj(o) {
    function F(){}
    F.prototype = o;
    return new F();
}

12. 实现构造函数继承

function Parent (name) {
    this.name = name;
}
function Child (name) {
    Parent.call(this, name);
}
var child1 = new Child('11');
console.log(child1.name); // 11
var child2 = new Child('22');
console.log(child2.name); // 22

13. 面向对象的属性

多态,封装,继承

14. 设计模式有哪些,项目中使用到哪些

  1. 单例模式,参考vuex,loading,全局弹窗等
  2. 策略模式,封装不同的计算方法,封装调用接口,常见购物车商品价格计算
  3. 观察者模式,参考webpack内部plugin的调用
  4. 发布订阅模式,参考vue响应式原理
  5. 迭代器模式,参考forEach,map方法,引申Iterator

15. 浏览器事件有哪些过程? 为什么一般在冒泡阶段, 而不是在捕获阶段注册监听? addEventListener 参数分别是什么 ?

过程:事件捕获阶段、处在目标阶段、冒泡阶段

IE低版本不支持捕获,为了兼容性,一般都写冒泡

addEventListener可接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。布尔值若为true,表示在捕获阶段调用事件处理程序;若为false,表示在冒泡阶段调用事件处理程序

16. new String('a') 和 'a' 是一样的么?

let data = 'a' // 定义的是一个基本类型字符创,入栈
let data = new String('a') // 定义的是一个包装类型字符串,入堆,

17. js中的装箱和拆箱了解吗?

装箱:把基本数据类型转化为对应的引用数据类型的操作。每当读取一个基本类型的时候,后台就会创建一个对应的基本包装类型对象,从而让我们能够调用一些方法来操作这些数据。
所以直接定义一个基础类型字符串也能调用方法。

var s1 = "abc";
var s2 = s1.indexOf("a")

拆箱:将引用类型对象转换为对应的值类型对象
如果是自定义的对象,你也可以自定义它的valueOf()或者toString()方法,实现对这个对象的拆箱。

var objNum = new Number(123);  
 var objStr =new String("123");   
 console.log( typeof objNum ); //object
 console.log( typeof objStr ); //object 
 console.log( typeof objNum.valueOf() ); //number
 console.log( typeof objStr.valueOf() ); //string  
 console.log( typeof objNum.toString() ); // string 
 console.log( typeof objStr.toString() ); // string

18. 节流与防抖原理

防抖与节流函数是一种最常用的 高频触发优化方式,能对性能有较大的帮助。

防抖 (debounce): 将多次高频操作优化为只在最后一次执行,通常使用的场景是:用户输入,只需再输入完成后做一次输入校验即可。

function debounce(fn, wait, immediate) {
    let timer = null
    return function() {
        let args = arguments
        let context = this
        if (immediate && !timer) {
            fn.apply(context, args)
        }
        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(context, args)
        }, wait)
    }
}

节流(throttle): 每隔一段时间后执行一次,也就是降低频率,将高频操作优化成低频操作,通常使用场景: 滚动条事件 或者 resize 事件,通常每隔 100~500 ms执行一次即可。

function throttle(fn, wait, immediate) {
    let timer = null
    let callNow = immediate
    return function() {
        let context = this,
            args = arguments;
        if (callNow) {
            fn.apply(context, args)
            callNow = false
        }
        if (!timer) {
            timer = setTimeout(() => {
                fn.apply(context, args)
                timer = null
            }, wait)
        }
    }
}

19. 面向对象和非面向对象有什么区别

面向对象是对事件处理的抽象,方便扩展设计,非面向对象一般是针对流程的处理,没有抽象概念。

20. 高阶函数是什么,怎么去写一个高阶函数

只需满足以下任意一个条件,即是高阶函数:

  1. 接受一个或多个函数作为输入
  2. return 返回另外一个函数

21. 模块化介绍一下,什么是编译时优化?模块化的演化过程?

AMD / CMD / ES6 module 主要是用于前端,都是属于异步并行加载

AMD是依赖前置,在define里先定义好依赖模块,然后在回调函数里做引用

CMD是依赖后置,define了一个模块,在里面什么时候用到模块了,什么时候再require

Commonjs一般运行在node端,通过复制module.exports这个属性,然后使用require对module.exports属性做复制,require属于是同步加载,require的模块是拷贝值,不会对引用模块产生任何改变。webpack里我们写的import最终也都是被转换成了commonjs的形式

es6 module 就是export和import,但是export出来的是一个接口,import只是用过接口对其内容进行引用,所以是可以修改起内容的。commonjs require是动态编译,运行时才加载,import是编译时就要加载,属于静态编译。但是目前已经有import()函数在提案内,可以实现动态加载了,此外还有更新的提案,await提升,之前await只能在标记async的函数内使用,现在的提案是await可以单独使用,import()动态加载时还需要写then做后续处理,现在不用了,写法简化很多。

22. 简单说一下PWA

PWA并不是单指某一项技术,你更可以把它理解成是一种思想和概念,目的就是对标原生app,将Web网站通过一系列的Web技术去优化它,提升其安全性,性能,流畅性,用户体验等各方面指标,最后达到用户就像在用app一样的感觉。

PWA中包含的核心功能及特性如下

  1. Web App Manifest
  2. Service Worker
  3. Cache API 缓存
  4. Push&Notification 推送与通知
  5. Background Sync 后台同步
  6. 响应式设计

23. call/apply/bind原理实现

call

Function.prototype.mycall = function(context) {
    // this 指向调用 mycall 的函数对象,调用 mycall 的若不是函数则报错
    if (typeof this !== 'function') {
        throw new TypeError('Not Function')
    }
    // context 为 null 或者 undefined 或者不传,则指向 window
    context = context || window
    // 将调用 mycall 的函数对象添加到 context 的属性中
    context.fn = this
    // 获取除第一个参数之外的其余参数,然后执行
    const args = [...arguments].slice(1)
    // mycall 的函数对象的 this 就指向了 context
    const result = context.fn(...args)
    // 删除该属性
    delete context.fn
    return result

}

apply

Function.prototype.myapply = function(context) {
    if (typeof this !== 'function') {
        throw new TypeError('Not Function')
    }
    if (arguments[1] instanceof Array) {
        throw new TypeError('Arguments Need To Array')
    }
    context = context || window
    context.fn = this
    let result = null
    // 如果不传参的话,arguments[1] 为 undefined,解构将会报错
    if (arguments[1]) {
        result = context.fn(...arguments[1])
    } else {
        result = context.fn()
    }
    return result
}

bind

Function.prototype.mybind = function(context) {
    if (typeof this !== 'function') {
        throw new TypeError('Not Function')
    }
    context = context || window
    const originFunc = this
    const args = [...arguments].slice(1)
    return function() {
        // 处理函数成为构造函数的情况
        // new.target 属性允许你检测函数是否是通过 new 运算符被调用的。在普通的函数调用中,new.target 的值是 undefined
        if (new.target) {
            // 构造函数 return 的是一个引用值的话,则 new 操作符返回的就是这个引用值
            return new originFunc(...args, ...arguments)
        } else {
            return originFunc.call(context, ...args, ...arguments)
        }
    }
}
  1. 操作dom有哪些方法?
  2. 怎么用原生js实现一个轮播图,以及滚动滑动?
  3. 怎么实现上传下载的功能?
  4. Map有哪些方法?
  5. 知不知道伪数组对象?

29. 如何用原生JS来读写Cookie?

const cookieArray = document.cookie;  //获取cookie存储字符串 
const arr = cookieArray.split(";");   //将获得的cookie字符串以;分割开获得单独每个cookie名/值对

// 读取
//假设要获取的cookie的名字是name,则遍历数组找到名字所对应的值  
let result = ''
for(var i=0;i<arr.length;i++){  
  var arr1 = arr[i].split("=");     //将名/值对以“=”分割开  
  if(arr1[0]==name){  
    result = arr1[1];  //如果名为name,则结果result为名对应的值  
  }
}
// 设置
document.cookie="name=value;expires=GMT_String";

30. 本地存储

31. 怎么打断点,如何确定一个结果来自于哪个函数

debugger

32. 简述自定义事件实现方法

33. parseInt() 和 array 的 map 方法的参数

34. JSON 对象的深度克隆

35. objec.freeze和Object.seal的区别

36. 编写函数 convert(money) ,传入金额,将金额转换为千分位表示法

37. js浮点数运算不精确,如何解决

我们进行运算的时候,实际上是把数字转换为了二进制进行的,所以我们把0.1和0.2转换为二进制:
0.1 => 0.0001 1001 1001 1001..(无限循环)
0.2 => 0.0011 0011 0011 0011…(无限循环)
这里可以看出转换为二进制是一个无限循环的数字,单在计算机中对于无限循环的数字会进行舍入处理的,进行双精度浮点数的小数部分最多支持52位。然后把两个2进制的数进行运算得出的也是一个二进制数值,最后再把它转换为十进制。保留17位小数,所以0.1+0.2的值就成了 0.30000000000000004。  0.1+0.1的值成了0.20000000000000000,全是0的时候可以省略,就成了0.2。
解决办法最简单的是直接toFixed,有时结果也是不准确的,所以我们还可以直接截取小数点后面的值直接乘以10的倍数,得到相加结果再除以相同的10的倍数。

38. 实现函数的柯里化

39. 前端存储方式,以及它们之间的优缺点

40. 怎么从十万个节点中找到想要的节点,怎么快速在某个节点前插入一个节点?

41. 如何找到一个字符串中最长的两个字符串?

42. 正则用过吗?exec, 匹配一个手机号?去空格?

43. 给你一亿个数,是连续的,怎么找出两个不存在的数

44. 怎么实现一个sleep ,手写一个promise

45. 做一个表格,一分钟刷新一次,怎么实现

46. 有了解WebWorker的实现原理吗,私下有实践过吗

直接来说,WebWorker可已开启多线程操作,但是不能操作dom。多线程之间可以通过postMessage传递信息,监听onMessages获取信息。

47. 平时做项目有没有考虑过内存的问题?怎么解决的?

48. 异步处理的方案有哪些?

数组

1. Array的unshift() method的作用是什么?如何连接两个Array?如何在Array里移除一个元素?

2. 数组去重

// set
function unique (arr) {
  return Array.from(new Set(arr))
}
// indexOf
function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    var array = [];
    for (var i = 0; i < arr.length; i++) {
        if (array .indexOf(arr[i]) === -1) {
            array .push(arr[i])
        }
    }
    return array;
}
// includes
function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    var array =[];
    for(var i = 0; i < arr.length; i++) {
            if( !array.includes( arr[i]) ) {//includes 检测数组是否有某个值
                    array.push(arr[i]);
              }
    }
    return array
}
// filter+indexOf
function unique(arr) {
  return arr.filter(function(item, index, arr) {
    //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
    return arr.indexOf(item, 0) === index;
  });
}

3. 数组乱序

function shuffle(arr) {
    let newArr = arr.map(item\=>({val:item,ram:Math.random()})); 
    newArr.sort((a,b)=>a.ram-b.ram); 
    let newArr = newArr.map(i=>i.val)
    return newArr; 
}
function shuffle(arr) { 
    let m = arr.length; 
    while (m > 1){ 
        let index = Math.floor(Math.random() * m--); 
        [arr[m] , arr[index]] = [arr[index] , arr[m]] 
    } 
    return arr; 
}

4. 有哪些排序算法,时间复杂度是多少?什么时候快排的效率最低?

5. 改变数组和不改变数组的方法分别是哪些?

6. 给定一个数组和一个正整数N,求一个和小于N的最长连续子数组

7. 给定两个有序数组,合并为一个有序数组。不许使用 js 的 concat 和 sort 方法

function arrSort(arr1, arr2) {
    var [i,j] = [0,0];
    let newArr = [];
    while(i < arr1.length || j <arr2.length) {
        if (arr1[i] < arr2[j]) {
            newArr.push(arr1[i]);
            i++
        } else if (arr1[i] > arr2[j]) {
            newArr.push(arr2[j])
            j++
        } else {
            if(arr1[i]) newArr.push(arr1[i]);
            if(arr2[j]) newArr.push(arr2[j]);
            i++;
            j++
        }
    }
    return newArr
}

8. sort()的原理

**当数组长度小于等于10的时候,采用插入排序,大于10的时候,采用快排。
对于长度大于1000的数组,采用的是快排与插入排序混合的方式进行排序的,因为,当数据量很小的时候,插入排序效率优于快排。**

9. 不产生新数组,删除数组里的重复元素

10. 冒泡排序和快速排序的区别

冒泡排序是从最底层元素开始比较du,(与其上的元素比较)小于就往上再zhi比,大于就交换,再用较小dao的往上比较,直到最高层,第一次把最小的放到最上层,第二次把第二小的放到第二层,以次类推。

快速排序是先找到一个轴值,比较时把所有比轴值小的放到轴值的左边,比轴值大的放到右边,再在两边各自选取轴值再按前面排序,直到完成。

ES6

  1. Promise的原理
  2. ES6 module
  3. 谈一下generator函数
  4. 说一下对async和await的理解
  5. Map有哪些方法?
  6. 箭头函数,this指向
  7. 扩展符的作用
  8. 新增数据类型有哪些?
  9. 新增的数据结构有哪些?
  10. ES6 里 let 和 var 的区别
  11. 解释ES6的暂时性死区

http

1. 讲一下AJAX Request

2. 跨域

同源策略导致跨域,解决办法: JSONP、nignx反向代理、CORS配置(会有一次预检请求)

3. 页面加载的过程(输入URL后发生了什么?)

4. TCP三次握手和四次挥手,拥塞控制

5. TCP和UDP的区别

6. HTTP请求头和响应头都有些什么字段?

7. SSL加密原理

非对称加密,对称加密

8. HTTP/HTTPS/HTTP2有什么区别?

9. HTTP2的优势

10. HTTP 里的304状态码了解吗?100和307?

304-延伸到浏览器缓存问题
100:continue 延伸到post请求
307:临时重定向,请求体不会发生变化

11. 进程通信,有名和匿名管道

进程之间通信,就实现手段上来看,有以下几种:

  1. 匿名管道(有亲缘关系进程)
  2. 有名管道(无亲缘也可以)
  3. 消息队列
  4. 共享内存
  5. 信号量
  6. socket套接字

匿名管道:

PIPE匿名管道用于具有血缘关系之间的进程进行通信。
1.半双工通信,同一时间数据只能向一个方向传输,具有固定的读端和写端。
2.只能用于具有亲缘关系的进程间通信(父子或者公共祖先)。
3.本质是内核中的一块缓冲区。
4.自带“同步互斥机制”,同时只有一个进程能够读到数据。
5.生命周期随进程。
6.管道提供字节流服务。

命名管道

FIFO:命名管道(也是半双工),每个FIFO都有一个路径名与之关联,从而允许无亲缘关系的进程访问同一个FIFO

特点:

  1. 可以在没有亲缘关系的进程间交换数据
  2. FIFO有路径名与之关联,它以一种特殊设备文件形式存在于文件系统中。
  3. 会出现抢读,写一次,就要去读,不管是谁读,里面的数据都会被清理
  4. 浏览器缓存

13. cookie、session、token的区别?

cookie由服务器生成,保存在客户端浏览器,容易被劫持,不安全
session保存在服务端,每个sessionID都是唯一的,当用户量太大时,占用服务器资源,较安全
token的工作原理:
1.客户端第一次请求时,发送用户信息到服务器。服务器对用户信息使用HSA256算法及密钥进行签名,再将这个签名和数据一起作为token返回给客户户端。
2.服务端不再保存token,客户端保存token。
3.当客户端再次发送请求时,在请求信息中将token一起发送给服务器。
4.服务器用同样的HSA256算法和密钥,对数据再计算一次签名,和token的签名做比较
5.如果相同,服务器就知道客户端登录过,则反之。

token的优势:
1.无状态、可扩展
2.支持移动设备
3.跨程序调用
4.安全

  1. 如果浏览器关闭了再打开, 请求还是from cache吗?

15. Service Worker 了解过么?

一个服务器与浏览器之间的中间人角色,如果网站中注册了service worker那么它可以拦截当前网站所有的请求,进行判断(需要编写相应的判断程序),如果需要向服务器发起请求的就转给服务器,如果可以直接使用缓存的就直接返回缓存不再转给服务器。从而大大提高浏览体验。

  • 基于web worker(一个独立于JavaScript主线程的独立线程,在里面执行需要消耗大量资源的操作不会堵塞主线程)
  • 在web worker的基础上增加了离线缓存的能力
  • 本质上充当Web应用程序(服务器)与浏览器之间的代理服务器(可以拦截全站的请求,并作出相应的动作->由开发者指定的动作)
  • 创建有效的离线体验(将一些不常更新的内容缓存在浏览器,提高访问体验)
  • 由事件驱动的,具有生命周期
  • 可以访问cache和indexDB
  • 支持推送
  • 并且可以让开发者自己控制管理缓存的内容以及版本
  • Service worker运行在worker上下文 --> 不能访问DOM
  • 它设计为完全异步,同步API(如XHR和localStorage)不能在service worker中使用
  • 出于安全考量,Service workers只能由HTTPS承载
  1. 为什么用 token 就可以防止 csrf 攻击?
  2. token 的刷新机制是怎么样的, 为什么这么设置?
  3. 静态文件的浏览器缓存如何实现

19. method有哪些方法,分别是什么意思?post和put的区别?post与get的区别?

method
get 查询数据
post 一般是对服务器的数据做改变,常用来数据的提交,新增操作
put put请求与post一样都会改变服务器的数据,但是put的侧重点在于对于数据的修改操作,但是post侧重于对于数据的增加
patch put的更新是全部更新,patch可以执行部分更新,只更新接收到的数据
delete 用来删除服务器的资源
options 属于浏览器的预检请求,查看服务器是否接受请求,预检通过后,浏览器才会去发get,post,put,delete等请求。非简单请求才会发送遇见请求

post和put的区别?
put请求与post一样都会改变服务器的数据,但是put的侧重点在于对于数据的修改操作,但是post侧重于对于数据的增加

post与get的区别?

  1. get是从服务器上获取数据,post是向服务器传送数据。
  2. 传参方式不同,一个参数挂在url上,一个参数在请求体里。
  3. get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。
  4. GET产生一个TCP数据包,POST产生两个TCP数据包。对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

20. Ajax 底层实现,readystate 有哪些

  1. 怎么实现标签页的通信
  2. OSI七层模型
  3. 反向代理知道么,Nginx
  4. 有没有了解过CDN
  5. 怎么实现标签页的通信

框架

  1. vue响应式原理
  2. vuex原理
  3. 组件间的通信方式及原理
  4. MVC和MVVM了解吗?有什么区别?
  5. Vue是怎么实现对数组变化的检测的

6. key的作用是什么?

key的作用主要是为了高效的更新虚拟DOM。另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它。
参考文章

  1. jsx的原理
  2. Virtual DOM 和 diff 算法
  3. 脚手架做了什么功能?
  4. vue-router原理
  5. Vue 里 v-if 和 v-show 的区别
  6. react生命周期
  7. react hook 相比较 class, 哪些不太容易实现?
  8. react 怎么做优化?
  9. 聊一下高阶组件 hoc
  10. 聊一聊组件设计, 领域模型
  11. redux原理
  12. setState 更新机制
  13. fiber架构
  14. react hook 原理
  15. 原生事件和 React事件的区别
  16. 高阶组件(HOC), Mixin, hook 对比和用处.
  17. webpack打包流程
  18. webpack如何处理图片、 CSS 文件?
  19. webpack做了什么优化?
  20. webpack热更新原理
  21. webpack 底层 Tapable 原理

28. webpack 如何实现异步加载?

就是把一些js模块给独立出一个个js文件,然后需要用到的时候,再创建一个script对象,加入到document.head对象中就可,浏览器会自动帮我们发起请求,去请求这个js文件,然后写个回调函数,让请求到的js文件做一些业务操作。

  1. babel原理
  2. transform-runtime有什么作用?

31. babel-runtime和babel-polyfill的作用和区别

Babel默认只转换新的JavaScript语法,而不转换新的API。 例如,Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign)都不会转译。 如果想使用这些新的对象和方法,则需要为当前环境提供一个polyfill

babel-polyfill

目前最常用的配合Babel一起使用的polyfill是babel-polyfill,它会”加载整个polyfill库”,针对编译的代码中新的API进行处理,并且在代码中插入一些帮助函数。

babel-runtime

babel-polyfill解决了Babel不转换新API的问题,但是直接在代码中插入帮助函数,会导致污染了全局环境,并且不同的代码文件中包含重复的代码,导致编译后的代码体积变大。
Babel为了解决这个问题,提供了单独的包babel-runtime用以提供编译模块的工具函数, 启用插件babel-plugin-transform-runtime后,Babel就会使用babel-runtime下的工具函数。垫片也是即用即插。这样可以避免自行引入polyfill时导致的污染全局命名空间的问题。

babel-polyfill与babel-runtime相比虽然有各种缺点,但在某些情况下仍然不能被babel-runtime替代, 例如,代码:[1, 2, 3].includes(3),Object.assign({}, {key: 'value'}),Array,Object以及其他”实例”下es6的方法,babel-runtime是无法支持的, 因为babel-runtime只支持到static的方法。

  1. babel 如何将字符串解析成 AST ?
  2. 讲一下AST 语法树
  3. webpack 和 gulp的对比
  4. node事件循环
  5. node架构中的容灾

37. node 的多线程,高并发,安全

由于node 是单进程的,无法充分利用多核 CPU 的性能,一般会使用 cluster 模块,进行多进程的部署。在多进程的环境下,又会带来一些并发的问题。进程之间的数据是不共享的,但是依然会有很多共享的资源,比如文件系统,数据库等。对于这些资源的并发访问和修改依然会导致问题。
所以在 cluster 模式下,需要更可靠的锁机制,保证对多个 node 实例,一次只能执行一个异步函数。为了实现这一点,仅仅依赖单线程的 node 是不可行的,需要引入外部的状态管理。Redlock 算法提供了一种基于redis 的分布式锁的实现。关于分布式锁和redlock算法的详细介绍可以参考这个链接https://redis.io/topics/distlock

38. 谈一下stream

39. pm2原理

40. 说说排查内存泄露的方法

内存泄露通常有两种情况,一种是容易复现的,一种是不容易复现的。
对于容易复现的我们在测试环境中模拟排查即可。
对于不容易复现的我们就要借助内存快照,可以使用devtool查看内存快照,heapdump保存内存快照。heapdump保存的内存快照只会有Node环境中的对象,如果使用node-inspector快照中就会包含前端变量,容易造成干扰。

41. 有没有读过 egg 源码

42. 小程序跟 h5 的区别是什么? [小程序底层实现]

43. 讲一下 taro 小程序的底层原理,跟 mpvue 的区别 [AST, babel

44. SPA 项目如何监控 pv, uv 值

45. 聊一下 axios .有什么优点, 跟 fetch, ajax对比

46. axios 为什么既可以在浏览器发请求,又可以在node层发请求?

47. 客户端渲染和服务端渲染的区别

监控

  1. 如何处理项目的异常
  2. error 怎么捕获?

3. SPA 项目如何监控 pv, uv 值?

使用导航守卫的钩子上报数据

4. 如何在用户刷新、跳转、关闭浏览器时向服务端发送统计的数据?

监听onload或beforeUnload,或者定义图片链接作为上传接口,除此之外,还可以使用navigator.sendBeacon(url, data);
sendBeacon方法具有如下特点:

  1. 发出的是异步请求,并且是POST请求,后端解析参数时,需要注意处理方式;
  2. 发出的请求,是放到的浏览器任务队列执行的,脱离了当前页面,所以不会阻塞当前页面的卸载和后面页面的加载过程,用户体验较好;
  3. 只能判断出是否放入浏览器任务队列,不能判断是否发送成功;
  4. Beacon API不提供相应的回调,因此后端返回最好省略response body

5. 错误日志上报遇到的问题.

6. 负载均衡方式和容错机制

7. 怎么计算在一个页面上的停留时间

移动端

1. 移动端如何优化首页白屏时间过长 ?

2. 移动端优化方式? 离线包是如何实现的?

3. 有几种webview?

ios8之前是UIWebview,ios8之后是WebkitWebview

4. webview和native的交互方式有哪些?

JavaScript 通知 Native

  • API注入,Native 直接在 JS 上下文中挂载数据或者方法

    • 延迟较低,在安卓4.1以下具有安全性问题,风险较高
  • WebView URL Scheme 跳转拦截

    • 兼容性好,但延迟较高,且有长度限制
  • WebView 中的 prompt/console/alert拦截(通常使用 prompt)

Native 通知 Javascript:

  • IOS: stringByEvaluatingJavaScriptFromString
  • Android: loadUrl (4.4-)
  • Android: evaluateJavascript (4.4+)

5. 怎么排查交互中的错误?

  1. chrome真机调试
  2. weinre调试
  3. spy-debugger调试
  4. vconsole.js
  5. APP内部调试接口

6. 浏览器内核有哪些,移动端用的是哪个?

7. 移动端页面适配解决方案

rem,viewport

安全

1. 前端安全方面有没有了解?XSS和CSRF如何攻防?

2. sql 注入知道么?

3. 爬虫与反爬虫

其他

1. 文件指纹怎么生成?

2. npx是什么?

npx是执行Node软件包的工具,它从 npm5.2版本开始,就与npm捆绑在一起。
npx 想要解决的主要问题,就是调用项目内部安装的模块。

  • 当在执行npx <command>的时候,npx会做什么事情?

    • 帮你在本地(可以是项目中的也可以是本机的)寻找这个 command

      • 找到了: 就用本地的版本
      • 没找到: 直接下载最新版本,完成命令要求
    • 使用完之后不会在你的本机或者项目留下任何东西

因此优势总结:

  • 不会污染本机
  • 永远使用最新版本的dependency

3. 说一下对 package.json 的理解,它都有哪些作用?

package.json文件描述了一个NPM包的所有相关信息,包括作者、简介、包依赖、构建等信息。格式必须是严格的JSON格式。
package.json文件指定了项目的版本,依赖文件,可运行脚本,入口文件等等

4. webgl用过吗?

5. svg和canvas的区别

6. git push -u 是什么意思

7. git rebase解释下,git merge 和git rebase区别

8. 能写一个二叉树么,怎么去遍历

9. 伪类知道吗,有哪些?

10. Xhtml和html的区别?

11. 二维码怎么工作的,扫描pc端的二维码,怎么让pc端登录?

12. 怎么做一个实时的聊天系统

13. 当消息有延迟的时候,怎么保证消息的正确顺序?

14. Mysql的基本写法

15. mysql的索引用的什么

16. mySql和noSQL区别

17. meta标签

18. 了解盒模型吗?

盒模型有两种:1. W3c标准的盒子模型(标准盒模型) 2. IE标准的盒子模型(怪异盒模型)
标准盒模型下盒子的大小 = content(width)+ padding + border + margin
怪异盒模型下盒子的大小 = content(width+ padding + border) + margin
box-sizing可以改变盒模型,border-box将会出发怪异模式解析计算

19. em,rem,px的区别

20. 简述动画写法

21. 维护的公共组件需要发布大更新, 如何做?

22. 聊一下微服务serverless?

23. 微前端了解吗?

24. 可视化表单了解过么?

25. typeScript了解过吗?

26. 平时处理过什么兼容性?


咸鱼在仰头
9 声望0 粉丝

It's made in heaven.