。。。

作者: 家有饿犬和聋猫 | 来源:发表于2019-10-28 20:03 被阅读0次

    https://juejin.im/post/5d23e750f265da1b855c7bbe

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

    react和vue都使用diff算法来对比新旧虚拟节点,从而更新节点。
    当新节点和旧节点从头到尾交叉对比没有结果时,会根据新节点的key对比旧节点数组中的key,如果没有找到就认为是新增了一个节点。
    如果没有key,那么就会采用循环遍历的方式查找就的节点
    在不带key的情况下,节点可以复用,省去dom操作的开销,但是只适用于无状态组件的渲染。
    带上dom虽然会增加开销,但是可以保证组件的状态正确,并且用户感受不到差距。

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

    返回1,NaN,NaN
    因为parseInt有三个参数,第一个是被处理的元素,第二个是该元素的索引。。。
    parseInt(“1”,0)按照十进制处理,返回 整数 1
    parseInt(“2”,1)按照1进制处理,2 不符合一进制表示法,所以返回NaN
    parseInt(“3”,2)按照2进制处理,3不符合二进制表示法,所以返回NaN

    3(挖财)什么是防抖和节流?有什么区别?如何实现?

    防抖:触发高频事件后n秒后调用一次函数,如果在n秒之内,高频事件再次被触发,则重新计算时间
    常见的比如input框输入与文本同步需要防抖
    解决思路: 创建一个标记用来存放定时器的返回值,每次事件触发前,都清除之前的延时调用
    然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数,只有在你停止输入的时候才会执行fn函数

    <input id="inpu" />
    
    function debounce(e) {
         let timeout = null; // 创建一个标记用来存放定时器的返回值
         return function () {
           clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
           timeout = setTimeout(() => { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
            console.log("e.target.value",e.target.value)
           }, 500);
         };
       }
      
    
       document.getElementById("inpu").addEventListener("keyup",debounce) // 防抖
    

    节流:高频事件触发,但在n秒内只会执行一次,稀释函数的执行频率
    思路:每次触发事件时都判断当前是否有等待执行的延时函数

    function throttle(fn) {
          let canRun = true; // 通过闭包保存一个标记
          return function () {
            if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return
            canRun = false; // 立即设置为false
            setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
              fn.apply(this, arguments);
              // 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
              canRun = true;
            }, 500);
          };
        }
        function sayHi(e) {
          console.log(e.target.innerWidth, e.target.innerHeight);
        }
        window.addEventListener('resize', throttle(sayHi));
    
    

    比如打王者荣耀,用户高频率点击输出时,机器只周期性输出,1秒输出一次
    总结:防抖和节流 都是防止某一时间函数高频率触发,但是他们俩的原理却不同
    防抖是每一时间段内只能触发一次,而节流是周期间歇性触发函数

    应用场景:
    防抖:1.search搜索联想,用户在不断输入值时,用防抖来节约请求资源。

    节流:1.鼠标不断点击触发,mousedown(单位时间内只触发一次)。
    2.监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断。

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

    Set(集合)
    成员唯一、无序且不重复
    [value, value],键值与键名是一致的(或者说只有键值,没有键名)
    可以遍历,方法有:add、delete、has
    WeakSet
    成员都是对象
    成员都是弱引用,可以被垃圾回收机制回收,可以用来保存DOM节点,不容易造成内存泄漏
    不能遍历,方法有add、delete、has
    Map (字典)
    本质上是键值对的集合,类似集合
    可以遍历,方法很多可以跟各种数据格式转换
    WeakMap
    只接受对象作为键名(null除外),不接受其他类型的值作为键名
    键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
    不能遍历,方法有get、set、has、delete

    5 介绍下深度优先遍历和广度优先遍历,如何实现?

    深度遍历就是自上而下的遍历,广度遍历就是逐层遍历
    深度遍历有回溯的操作(没有路走了需要回头),
    深度优先采用的是堆栈的形式, 即先进后出
    广度优先则采用的是队列的形式, 即先进先出

       const data = [
        {
            name: 'a',
            children: [
                { name: 'b', children: [{ name: 'e' }] },
                { name: 'c', children: [{ name: 'f' }] },
                { name: 'd', children: [{ name: 'g' }] },
            ],
        },
        {
            name: 'a2',
            children: [
                { name: 'b2', children: [{ name: 'e2' }] },
                { name: 'c2', children: [{ name: 'f2' }] },
                { name: 'd2', children: [{ name: 'g2' }] },
            ],
        }
    ]
    
    

    深度遍历(递归调用)

    function  getName(data){
        let result=[];
         data.forEach(
               item=>{
                 
                const  maps=(data)=>{
                    result.push(data.name)  
                    //对象里的数组如果存在就继续调用
                    data.children&&data.children.forEach(
                        p=>maps(p)
                    )
                }
               
                maps(item)
               }
         )
      return  result.join(",")  
    }
        console.log(getName(data))
     //a,a2,b,c,d,b2,c2,d2,e,f,g,e2,f2,g2
    

    广度遍历(数组循环)

    function getNames(data){
    
       let result=[];
       let queue=data ;
       while(queue.length>0){
            [...queue].forEach(
                p=>{
                    queue.shift();
                    result.push(p.name)
                    p.children&&(queue.push(...p.children))
                }
            )
       }
       return result.join(",")
    }
    console.log(getNames(datad))
    //a,a2,b,c,d,b2,c2,d2,e,f,g,e2,f2,g2
    
    6 :请分别用深度优先思想和广度优先思想实现一个拷贝函数?

    我不会做

    7 ES5/ES6 的继承除了写法以外还有什么区别?

    es6 :
    class 声明会提升,但不会初始化赋值。Foo 进入暂时性死区,类似于 let、const 声明变量。
    class 声明内部会启用严格模式。
    class 的所有方法(包括静态方法和实例方法)都没有原型对象 prototype(只有proto),所以也没有[[construct]],不能使用 new 来调用。 class 内部无法重写类名。

    8 setTimeout、Promise、Async/Await 的区别

    宏观任务和微观任务


    image.png

    Promise本身是一个立即执行函数,如果带then或者catch,有一步操作功能
    async 函数返回一个 Promise 对象,await配合async触发异步操作,控制函数的执行顺序。async可以单独使用,await不可以。

    8 (头条、微医)Async/Await 如何通过同步的方式实现异步
     function fun1() {
       console.log(1);
       console.log(2);
    }
     function fun2() {
        console.log(3);
        console.log(4);
    }
    // async 实现的是一个异步操作,await 等待一个异步方法执行完成。
    // async内使用await等待async的执行完成,就形成了异步函数的同步实现。
    async function fun3() {
      await fun1();
      await console.log(5);
      await fun2();
    }
    fun3();
    
    9 // 编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组
     var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
     写法一:  console.log("整理后", [...new Set(arr.flat(Infinity))].sort((a, b) => a - b))
                 //  整理后 (14) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
    写法二 :  数据扁平化写法不同,其他都一样
           1    let arrs = JSON.stringify(arr );
           2    扩展运算符:
                while (ary.some(Array.isArray)) {
                    ary = [].concat(...ary);
                  }  
          3 递归调用:
                   function maps(arr){
                arr.map(
                    p =>{ 
                        if(p.constructor ===  Array){
                                return    maps(p)
                        }else{
                            data.push(p)
                        }
                    }
             )
            }
               
    
    10 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方法是异步执行的
    扩展 :

      console.log(1);
      resolve(5);
      console.log(2);
    }).then(val => {
      console.log(val);
    });
    
    promise.then(() => {
      console.log(3);
    });
    
    console.log(4);
    
    setTimeout(function() {
      console.log(6);
    });
    
    // 124536
    

    promise是微观任务,setTimeout是宏观任务,先执行微观任务,在执行宏观任务;微观任务里,先执行同步再执行异步 所以结果是 124536

    11 如何实现一个 new
    // 实现一个new
    var Dog = function(name) {
      this.name = name
    }
    Dog.prototype.bark = function() {
      console.log('wangwang')
    }
    Dog.prototype.sayName = function() {
      console.log('my name is ' + this.name)
    }
    let sanmao = new Dog('三毛')
    sanmao.sayName()
    sanmao.bark()
    //打印:  wangwang
    // my name is 三毛
    

    new的作用:1 创建一个新对象
    2 新对象(实例)的proto指向构造函数的prototype ,实现继承
    3 执行构造函数,传递参数,改变this指向 Dog.call(obj, ...args)
    4 最后把new出来的对象赋值给 变量sanmao

    相关文章

      网友评论

          本文标题:。。。

          本文链接:https://www.haomeiwen.com/subject/xtjiectx.html