”js是一门单线程的语言,js最大的特性是异步“,这些说法已经见惯不怪了,然而不了解js的解释执行始末,这些概念也就只是听听,真正遇到问题的时候,也只能一脸懵逼而已~javascript
因为 Js 是一门单线程的语言,为了实现异步特性,必须有一种行之有效的机制,Event Loop 就是这种机制java
说 Js 是一门单线程语言,指的是它只有一个用户执行线程,同一时刻只能执行一个任务,在你看不见的地方,还须要有不少其它线程/进程来调度ajax
当程序开始运行的时候,将默认执行全局执行上下文(将在后文中说明)中的代码,若是遇到同步函数,那么将把当前执行上下文压入栈中,进入同步函数的执行上下文执行(假若在函数中又遇到了其它同步函数,将持续此过程),当函数执行完成,执行线程将从消息线程中pop一个执行上下文进行执行网络
global() {
/** * 定义 */
EC3() {
}
EC2() {
EC3(); //调用EC3
}
EC1() {
EC2();//调用EC2
}
//调用EC1
EC1();
}复制代码
若是当前执行上下文遇到了异步操做,那么它向事件监听线程或者计时线程发出通知以后,将继续执行,此时有两种状况:
a) 计时函数,如:闭包
setTimeout(func, 1000);复制代码
此时,异步通知将发给计时线程(Timer),一秒后,计时线程将会把 func
函数的执行上下文压入消息线程堆栈,执行线程在处理完当前执行上下文的时候从消息线程堆栈中 Pop 出 func 的执行上下文进行执行(若是在此期间没有别的执行上下文入栈的话)异步
因此,
setTime(func, n);
并不能保证,在 n 毫秒以后 func 能被执行,这还得看执行线程当前在干什么了函数
b) 事件监听线程,若是发起的是一个异步io操做,如发起一个网络请求:oop
$.ajax(url, params, callback);复制代码
那么异步事件将发给事件监听线程去监听,一旦网络请求完成,一样的,事件监听线程将把 callback 的执行上下文入栈,等待执行线程的召唤ui
上文反复提到执行上下文,下面就对执行上下文进行一个深刻剖析this
所谓执行上下文,就是 Js 执行的时候的一个运行环境/做用域(scope),有以下几种状况:
/** * 全局执行上下文/做用域 */
console.log('在全局环境中执行')
function hello() { //hello函数执行上下文/做用域
var say = 'hello';
function world() { //world函数执行上下文
...
}
}复制代码
全局做用域中的方法、变量,能够被其它任何函数做用域所访问,函数做用域中的方法变量,在子函数做用域中能够访问,外部没法直接访问
经过函数返回的子函数去访问函数做用域的私有变量,也就造成了闭包
也就是上文提到的 Event Loop,该线程以栈的形式保存执行上下文,函数执行上下文的入栈出栈的过程,使得js得以在单线程的状况下,实现异步特性
每一次函数被调用,js解释器都会为之建立新的上下文,此时能够分为两个阶段:
a) 上下文的建立阶段:函数被调用,但还没有开始执行(代码分析预处理阶段),此时会为执行上下文建立做用域链,建立变量、函数和参数以及求this的值
executionContextObj = {
scopeChain: { /* 变量对象(variableObject)+ 全部父执行上下文的变量对象*/ },
variableObject: { /*函数 arguments/参数,内部变量和函数声明 */ },
this: {}
}复制代码
特别的,变量提高就是在这个阶段发生的
b) 执行阶段:指派变量的值和函数的引用并解释执行代码
下面再用伪代码的形式来描述一下这个过程:
//函数被调用
1. 建立执行上下文
a) 建立做用域链
b) 建立变量、函数和参数
c) 求this值
2. 开始执行在执行上下文上 执行
...
a) 遇到同步函数
b) 当前执行上下文入栈
c) 重复以上过程
...
3. 执行完成,往上一层 执行上下文 返回数据
4. 从执行上下文栈pop出一个新的执行上下文执行复制代码
了解 Js 解释器一些底层原理,是很是有必要的,当程序的运行结果跟你的预期结果不同的时候,甚至看起来很诡异的时候,如何去解释,就很体现能力了
更多精彩,邀您关注~~