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

 

posted on 2020-06-18 15:49  半夏微澜ぺ  阅读(373)  评论(0)    收藏  举报