JS执行上下文
执行上下文,即context,也不知道是谁翻译的,不少的文献、书籍用的都是这个词。还记得第一次接触这个词时的惆怅、迷惘、不知所措,扶了扶眼镜,翻开大辞典,还是翻译成环境比较接地气。JS执行上下文,即JS的执行环境。
执行环境(Execution Context, EC)
当我们的代码执行时,会进入到不同的执行上下文,即不同的环境。在不同的环境中,有着不同的 scope(作用域),代码所能访问到的资源也就不同。 在 JS 中,执行环境有如下三种情况:
-
全局环境
代码默认运行的环境,代码执行时会首先进入全局环境。它是最外围的一个执行环境,根据 ECMAScript 实现所在的宿主环境的不同,表示全局环境的对象也不一样。在 web 浏览器中,全局环境就是 window 对象。全局变量和函数都是作为全局对象 window 的变量和方法来创建的。
-
函数环境
函数被调用执行时,所创建的执行环境。
-
eval
使用 eval 会进入一个新的执行环境,它的变量对象为全局变量对象或调用者的变量对象。由于 eval 的毒瘤属性,一般不推荐使用,可忽略。
执行环境的生命周期
执行环境的生命周期大概分为两个阶段,即创建阶段和执行阶段:
1. 创建阶段
- 创建作用域链(变量对象+父级执行环境的变量对象)
- 创建变量对象(包括局部变量、函数以及函数参数)
- 确定 this 的指向
由此,一个执行环境可以由包含作用域链、变量对象和 this 指针的对象组成:
executionContextObj = {
scopeChain: {},
variableObject: {},
this: {}
}
2. 代码执行阶段
- 指定变量的值和函数的引用
- 解释并执行代码
执行环境栈(Execution Context Stack, ECS)
浏览器中的解释器被实现为单线程,同一时间只能处理一个任务,JS 程序中多个执行环境会以栈的方式来处理,这个栈叫做执行栈。栈底永远都是全局环境(窗口关闭时弹出),栈顶就是当前正在执行的环境。前述三种情况都会创建执行环境,执行环境创建时会被压入栈顶,成为一个运行(活动)的环境,位于栈顶的环境执行完毕后就从栈顶弹出,并将环境控制权交给调用者(之前的栈),而调用者继续执行(或激活其他环境),直到它的执行环境结束。ECMAScript 程序中的执行流正是由这个方便的机制控制着。
来看下面的例子:
var firstName = 'snow';
function getName() {
var lastName = 'John';
function fullName() {
var name = lastName + firstName;
return name;
}
var name = fullName();
return name;
}
getName();
其执行栈变化过程如下:
-
首先,将全局环境压入栈,开始执行代码,
-
直到遇到
getName()
,准备调用函数,创建函数 getName 的执行环境,将其压入栈顶并开始执行函数 -
直到遇到
fullName()
,准备调用函数,创建函数 fullName 的执行环境,将其压入栈顶并开始执行函数 -
函数 fullName 执行时没有再生成执行环境,执行完毕后则从栈顶弹出
-
fullName 执行栈弹出后,控制权回到了 getName 的执行栈,继续执行代码,执行完毕,从栈顶弹出
-
最后回到了全局环境,窗口关闭后弹出
结论
- 单线程,同步执行,只有栈顶的环境处于执行中,其余环境需要等待。
- 执行 JS 程序,首先进入全局环境,全局环境只有一个并在关闭窗口时弹出。
- 函数调用时会创建新的执行环境,包括调用自己。
转载自:https://juejin.cn/post/6844903810310111239