异步操作顺序、深拷贝| 浅拷贝

620 阅读4分钟

html、css 这些基础知识,自行查找。这里只分享干货。

js常见的例题

数据类型 6 + 3
  1. 值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。

  2. 引用数据类型:对象(Object)、数组(Array)、函数(Function)。

关于 promise、setTimeout、异步执行的顺序问题。
    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');

答案: script start / async1 start / async2 / promise1 / script end / async1 end / promise2 / setTimeout

解析:js是基于事件驱动单线程执行的,执行顺序 代码自上而下。

  1. 第一步: script start , 虽声明了两个函数,但都没调用,因此 输出 “ script start” ;

  2. 第二步: async1 start , 执行到setTimeout时,但并不会打印 setTimeout。会直接走到下一步,执行async1() ;

    • 解析为什么并不会打印setTimeout ?

      1. 我们知道,JavaScript是基于事件驱动单线程执行的,所有任务都需要排队,也就是说前一个任务结束,才会去执行下一个任务。而像setTimeout、ajax等异步操作的回调,会进入”任务队列“中,而且只有主线程中没有执行任何同步代码的前提下,才会执行异步回调。而setTimeout(fn, 0)表示立即执行,也就是用来改变任务的执行顺序,要求浏览器尽可能快的进行回调。

      2. 虽然参数2 为0,但并不代表这0秒之后执行setTimeout的代码,正确的解释是,0秒之后你可以执行了,但并不意味着你可以执行,只是代表这 浏览器将你这个任务接收了,已经插入到任务队列当中了,但必须需要等待我所有程序执行完毕才可以执行此任务。

      3. 0秒其实是假象,setTimeout有个最小执行时间4ms minimum delay of 4ms

        注:HTML5中已经将最小执行时间统一为4ms。

      4. 这里有一个例子可更清晰的解释;参考文章 你应该知道的setTimeout秘密

            var start = new Date();  
            var end = 0;  
            
            setTimeout(function() {   
              console.log(new Date() - start);  
            },  500);  
            
            while (new Date() - start <= 1000) {}
            
            你是不是觉得打印结果为:500,
            然而并不是,打印结果为 : 1004 (也许你打印的不一样,但肯定大于 1000)
            
            虽然setTimeout的延时时间是500毫秒,可是由于while循环的存在,只有当间隔时间大于1000毫秒时,
            才会跳出while循环,也就是说,在1000毫秒之前,while循环都在占据着JavaScript线程。
            也就是说,只有等待跳出while后,线程才会空闲下来,才会去执行之前定义的setTimeout。
            
            最后 ,我们可以总结出,setTimeout只能保证
            在指定的时间后将任务(需要执行的函数)插入任务队列中等候,
            但是不保证这个任务在什么时候执行。
            一旦执行javascript的线程空闲出来,自行从队列中取出任务然后执行它。
            
            实在不理解,去看上述 文章。
        
  3. 第三步:async1 start ,setTimeout将任务进行等待,所以执行函数async1 ()

  4. 第四步: async2 ,继续执行async1() 函数的结果,且返回promise对象

  5. 第五步: promise1, async1()函数中包含关键词await,'async1 end' 并不会执行,会一直等待其他非await程序执行完毕。 new Promise() 是立即执行函数,所以执行 promise1, promise2不执行和 上述同理

  6. 第六步: script end , 因为上一步先打印了promise1,然后执行到resolve的时候,然后跳出promise继续向下执行,输出script end

  7. 第七步:async1 end, 返回promise对象开始执行。

  8. 第八步: promise2 , 返回promise对象开始执行。

  9. 第九步: setTimeout,最终才执行。因为所有任务已经执行完毕。

深拷贝和浅拷贝

本文思维导图如下:

深拷贝方法:

  1. 简单方法: JSON.parse(JSON.stringify( )) ;
  2. 递归
   /**
    * 递归深拷贝
    * */
  function deepClone(origin) {
   let target = origin ;
   let isDate = Object.prototype.toString.call( origin ) === '[object Date]' ;
   let isRegExp = Object.prototype.toString.call( origin ) === '[object RegExp]' ;

   if(typeof origin === "object" && origin !== null && !isDate && !isRegExp){
       target =  Array.isArray(origin) ? [] : {} ;
       for(let key in origin){
           if(origin.hasOwnProperty(key)){
               target[ key ] = deepClone(origin[key])
           }
       }
   }
   return target ;
}