zoukankan      html  css  js  c++  java
  • js代码执行以及宏任务和微任务-语法检测-预编译-变量提升

    js代码是单线程的,同一时间不可能同时运行两个js代码,js中的异步不是js的异步而是浏览器的异步。一些i/o操作 定时器额计时和事件监听等都是由浏览器提供的其他线程来完成的。

    执行过程:

    1 所有的同步任务都在主线程上执行,形成一个执行栈。

    2 主线程之外,还存在一个‘任务队列’,只要异步任务有了运行结果,就在任务队列之中放置一个事件。

    3 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取‘任务队列’,看看里面有哪些事件。那些对应的异步任务。于是结束等待状态,进入执行栈,开始执行。

    4 主线程不断重复上面的第三步

    事件队列会将处理事件的优先级进行排序,在通过执行栈来执行事件

    其实同步和异步,无论如何,做事情的时候都是只有一条流水线(单线程)

    浏览器线程

    浏览器异步:

    事件监听-addEventListener

    定时器-setTimeout、setInterval

    ajax-异步请求

    GUI渲染也是在引擎线程中执行的,脚本中执行对界面进行更新操作,如添加节点,删除节点或改变节点的外观等更新并不会立即体现出来,这些操作将保存在一个队列中,待js引擎空闲时才有机会渲染出来。

    异步操作,触发来源于js代码,执行在浏览器的其他线程,回调函数又加入js队列。

    宏任务和微任务

    • 同步和异步任务分别进入不同的执行“场所”,同步的进入主线程,异步的进入Event Table 并注册函数

    • 当指定的事情完成时,Event Table 会将这个函数移入Event Queue

    • 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行

    • 上述过程会不断重复,也就是常说的Event Loop(事件循环)

    • setTimeout这个函数,是经过指定时间后,把要执行的任务加入到Event Queue中,又因为是单线程任务要一个个执行,如果前面的任务需要的时间太长,那么只能等着。

    广义的同步任务和异步任务

    • 宏任务:包括整体代码 script,setTimeout,setIntervcal

    • 微任务:Promise,process.nextTick

    不同类型的任务会进入对应的Event Queue,比如setTimeout和setInterval会进入相同的Event Queue

    事件循环的顺序,决定js代码的执行顺序。进入整体代码后,开始第一次循环。接着执行所有的微任务

    然后再次从宏任务开始,找到其中一个任务队列执行完毕,在执行所有的微任务

    js执行顺序

    第一步:检查语法错误

    浏览器或者mode环境将所有的js检查一遍,检查是否有语法错误,注意并不会执行,这里是确保可以执行,然后进行第二部预编译

    第二步:预编译

    预备知识:变量提升
    首先要理解函数声明整体提升,变量声明提升;
    这里要注意变量的提升,一般我们声明一个变量都是:
    var a=1;
    console.log(a);//1
    //但实际上拆分成了两步,真正执行时这样的
    var a;
    a=1;
    console.log(a);//变量声明提升
    如果把console.log放在上面:
    console.log(a);//undefined
    var a=1;
    实际上执行的代码://变量声明提升,赋值并不会提升
    var a;
    console.log(a);
    a=1;

    预编译步骤:

    1 预编译的时候会创建一个AO对象(Activation Object)执行上下文;
    2 找形参和变量声明,将形参和作为AO对象的属性名,值为undefined;
    3 将形参和实参统一
    4 在函数体里找函数声明,值赋给函数体
    注意:AO按如下顺序填充:
    函数参数(若有传值,会被赋值,若为赋值,初始化值为undefined)优先级第二
    函数声明(若发生命名冲突,会覆盖)优先级最高
    变量声明(初始化变量值为undefined,若发生命名冲突,会忽略)优先级第三

    具体的步骤:

    0:函数的在运行的瞬间,生成一个活动对象(Active Object)就是所谓的AO

    1:分析参数

          1-1:函数接收参数,添加到AO的属性上面,值全部都是undefine,如AO.age=undefine

          1-2:接收实参,形成AO对应的属性值

    2:分析变量声明,如var age,

         2-1:如果AO上还没有age属性,则添加AO 属性,值是undefine

         2-2:如果AO 上面已经有了age属性,则不做任何操作。

    3:分析函数的声明,如果funcion foo(){},

    3-1: 则把函数赋给AO.fooo,如果数据属性已经存在,则要被现在的新的值覆盖

    最后开始按优先级赋值

    demo1:
    function fn(a){
    var a=10;
    console.log(a);
    }
    fn(11);//10
    fn(11)实际执行代码:
    a=11;
    var a=10;
    console.log(a);

    demo2:

    function fn(a){
    console.log(a);
    var a=11;
    function a(){console.log('我优先级最高')}
    }
    fn(10);//[funciton:a]
    //函数声明的优先级最高
    实际执行代码:
    a=10;
    function a(){console.log('我优先级最高')} console.log(a);var a=11;

    demo3:

    function fn(a){:
    var a=11;
    function a(){console.log('我优先级最高')}
    console.log(a);
    }
    fn(10);//11

    // 注意,与上一段代码不同的是,在a被打印出来之前,
    // a 经历的成长是:
    // 1.形参a传过来,AO对象创造属性 AO.a = undefinded
    // 2.结合实参,AO.a = 10
    // 3.变量声明,它本应该在AO对象内创造 a 属性并赋值为undefinded,但a属性已经存在,不做改动
    // 4.遇到函数声明,a 被赋值为一个函数体,即由 a = 10 变为 a = Function
    // 5.开始执行函数,遇到 var a = 11 这一句,a 被赋值为 11,即 a = Function 变为 a = 11

    demo4:
    function fn(a){
    console.log(a);
    var a=11;
    console.log(a);
    function a(){console.log('我优先级最高')}
    }
    fn(10);//[funciton:a] 11
    2.然后就到了函数的执行阶段, 此阶段当前函数中使用到的所有变量和函数声明都会从当前函数的[[Scope]]作用域链中查找, 根据作用域链中对象的位置首先会查找当前函数的AO对象, 如果没有再查找上层对象, 最后找到全局对象, 如果都没有则会报错(变量未定义).
     
     
     
     
     

    参考1:https://www.cnblogs.com/wangziye/p/9566454.html

    参考2:https://www.jianshu.com/p/2a5954b78ff5

    参考3:https://blog.csdn.net/guolinengineer/java/article/details/84984498

  • 相关阅读:
    如何在Eclipse中查看Java类库的源代码以及相应的api
    深入剖析ConcurrentHashMap
    Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析
    Quartz配置
    Spring 自动定时任务配置
    @Resource注解的官方解释
    @Resource 注解的使用
    扫地机器人会否抛弃激光雷达这位原配?
    女教授领军打造最耐用机器人,可从180米高空落下执行救援任务
    一文看懂80年“AI革命”简史
  • 原文地址:https://www.cnblogs.com/xiaofenguo/p/13157928.html
Copyright © 2011-2022 走看看