面试题整理(每日一题)(2021/02/12)

125 阅读4分钟

1、写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?

1)不写key

    + 就地复用节点:在比较新旧两个节点是否是同一个节点的过程中会判断成新旧两个节点是同一个节点,因为 a.key 和 b.key 都是 undefined。所以不会重新创建节点和删除节点;所以可能在某种程度上(创建和删除节点方面)会有渲染性能上的提升;
    + 但是只是针对无状态的组件来说;对于有状态的组件,如果复用可能会出现一些未知的bug;
    
(2)写key

+ 维持组件的状态,保证组件的复用。因为有 key 唯一标识了组件,不会在每次比较新旧两个节点是否是同一个节点的时候直接判断为同一个节点,而是会继续在接下来的节点中找到 key 相同的节点去比较,能找到相同的 key 的话就复用节点,不能找到的话就增加或者删除节点。

+ key的作用是为了在diff算法执行时更快的找到对应的节点,提高diff速度;
为了增强复用性,在diff两个新老list的时候采用四种命中查找的方式:
新前==旧前
新后==旧后
新后==旧前
新前==旧后
这样查找如果相同就复用节点;
如果上面的四种同级比较策略没有找到可以复用的 dom 元素;会采用map的方式,将旧的list中剩下的元素弄成一个map,key为每个节点的key,value是索引,然后新的节点在这个map中寻找,如果没有就进行创建,如果有就复用;如果没有key,需要循环去遍历,耗费性能;

2、['1', '2', '3'].map(parseInt) what & why ?

parsetInt([val],[radix])处理机制:
  (1[val] 必须是一个字符串,如果不是,则也要默认转换为字符串
  (2[radix]不设置(或者写的是零):正常都是按照10处理的,如果字符串是以”0x“开始的,默认值是16...
  (3)先在[val]中,找到所有符合[radix]进制的内容(从左到右查找,直到遇到不符合的为止「不论后面是否还有符合进制的,都不在查找了」),然后再把找到的内容看做[radix]进制,转换为十进制
  (4[radix]范围  2~36,除了0以外(0->10/16),不在这个范围内,结果都是NaN

3、什么是防抖和节流?有什么区别?如何实现?

(1)防抖:触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间

github.com/mqyqingfeng…

function debounce(func, wait) {
    var timeout;

    return function () {
        var context = this;
        var args = arguments;

        clearTimeout(timeout)
        timeout = setTimeout(function(){
            func.apply(context, args)
        }, wait);
    }
}

(2)节流:高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率

github.com/mqyqingfeng… function throttle(func, wait) { var context, args; var previous = 0;

    return function() {
        var now = +new Date();
        context = this;
        args = arguments;
        if (now - previous > wait) {
            func.apply(context, args);
            previous = now;
        }
    }
}

4、介绍下 Set、Map、WeakSet 和 WeakMap 的区别?

Set
成员唯一、无序且不重复
[value, value],键值与键名是一致的(或者说只有键值,没有键名)
可以遍历,方法有:adddelete、has

WeakSet
成员都是对象
成员都是弱引用,可以被垃圾回收机制回收,可以用来保存DOM节点,不容易造成内存泄漏
不能遍历,方法有adddelete、has

Map
本质上是键值对的集合,类似集合
可以遍历,方法很多可以跟各种数据格式转换

WeakMap
只接受对象作为键名(null除外),不接受其他类型的值作为键名
键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
不能遍历,方法有getset、has、delete

map与其他的数据结构相互转换

7、ES5/ES6 的继承除了写法以外还有什么区别? juejin.cn/post/684490…

8、setTimeout、Promise、Async/Await 的区别?

9、Async/Await 如何通过同步的方式实现异步?

juejin.cn/post/692838…

 Async Await源码实现

10:异步笔试题

请写出下面代码的运行结果

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
async function async2() {
    console.log('async2');
}
console.log('script start');
setTimeout(function() {
    console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
});
console.log('script end');

11、将数组扁平化并去除其中重复数据,最终得到一个升序且不重复的数组

Array.from(new Set(arr.flat(Infinity))).sort((a,b)=>{ return a-b})

12、JS 异步解决方案的发展历程以及优缺点?

13、Promise 构造函数是同步执行还是异步执行,那么 then 方法呢?

 const promise = new Promise((resolve, reject) => {
  console.log(1)
  resolve()
  console.log(2)
})

promise.then(() => {
  console.log(3)
})

console.log(4)
执行结果是:1243

promise构造函数是同步执行的,then方法是异步执行的

14、手撕new

 function myNew(Fun) {
    var obj = Object.create(Fun.prototype),
        params = [].slice.call(arguments, 1),
        result = Fun.apply(obj, params)
    if (typeof result === 'object' || typeof result === 'function') {
        return result
    }
    return obj
}
function Fun(a) {
    this.a = a
}
console.log(myNew(Fun, 3))

27、关于 const 和 let 声明的变量不在 window 上

classlet定义的即便是全局对象,但不是顶层对象的属性,在window自然获取不到,只能去定义变量的块级作用域里获取