理解执行上下文
只有理解了执行上下文和调用栈,才能更好地理解JavaScript本身,更好地理解一些复杂的概念,如变量提升、作用域和闭包等。尽管面试时,面试官不会直接让你回答执行上下文的概念,但会重点考察变量提升、闭包的相关题目。
查阅资料发现,ES3、ES6对于执行上下文的介绍不同。
ES3从变量对象(Variable Object )、作用域链(Scope chain)、this指向的角度介绍执行上下文。
ES6从词法环境、变量环境、this指向的角度介绍执行上下文。
本文主要介绍ES6中的执行上下文,如果想了解ES3对执行上下文的介绍,可以参考JavaScript深入之执行上下文。
什么是执行上下文
执行上下文是JavaScript执行一段代码时的运行环境(Execution context),确定代码执行期间用到的如this,变量,对象以及函数等。
执行上下文由词法环境组件
、变量环境组件
和this绑定
三部分组成。如下表所示:
组件 | 作用目的 |
---|---|
词法环境组件 | 指定一个词法环境对象,用于解析该执行环境内的代码创建的标识符引用。 |
变量环境组件 | 指定一个词法环境对象,其环境数据用于保存由该执行环境内的代码通过VariableStatement和FunctionDeclaration创建的绑定。 |
this绑定 | 指定该执行环境内的ECMA脚本代码中的this关键字所关联的值。 |
词法环境组件: 通过let const with try-catch 创建的变量存在这里。
变量环境组件: 通过var声明或function{} 声明的对象存在这里。
来看一个执行上下文的例子:
var a = 1;
let b = 2;
function printName (name) {
console.log(name)
}
当运行这段代码的时候,就会产生类似上图的执行上下文,a是通过var声明的,会被放到变量环境中;
b是通过let声明的,会被放到词法环境中;printName是一个函数声明,因此会被放到变量环境中;这里可以看到会产生变量提升的变量都被放到了变量环境中去了。
注意词法环境组件和变量环境组件都指定了一个词法环境对象
。
在JavaScript中,每个运行的函数,代码块以及整个脚本,都有一个被称为词法环境(Lexical Environment)的内部(隐藏)的关联对象。
词法环境对象由两部分组成:
环境记录
(Environment Record): 一个存储所有局部变量作为其属性的对象(包括一些其他信息,例如this的值)。- 对
外部词法环境
的引用,与外部代码相关联。
什么时候会创建执行上下文
- 全局执行上下文
- 当JavaScript执行全局代码的时候,会编译全局代码并创建全局执行上下文,而且在整个页面的生存周期内,全局执行上下文只有一份。
- 函数执行上下文
- 当调用一个函数的时候,函数体内的代码会被编译,并创建函数执行上下文,一般情况下,函数执行结束之后,创建的函数执行上下文会被销毁。
- eval函数
- 当使用eval函数的时候,eval的代码也会被编译,并创建执行上下文。
只有在代码运行的时候才会产生执行上下文。
什么是执行上下文栈
首先要理解什么是栈,栈是一种后进先出的数据结构。
运行全局代码的时候,会产生全局执行上下文;当调用一个函数的时候,会创建一个函数执行上下文,那么JavaScript引擎是如何管理这么多执行上下文的呢?没错,就是通过栈这种数据结构,通常称为调用栈
。
当JavaScript运行一段代码时,会先将全局执行上下文压入栈底,当调用一个函数的时候,会将创建的执行上下文压入调用栈中,当函数执行完成,会将该执行上下文从调用栈中弹出。