JavaScript的执行上下文

138 阅读4分钟

理解执行上下文

只有理解了执行上下文和调用栈,才能更好地理解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)
}
vueresponse

当运行这段代码的时候,就会产生类似上图的执行上下文,a是通过var声明的,会被放到变量环境中;

b是通过let声明的,会被放到词法环境中;printName是一个函数声明,因此会被放到变量环境中;这里可以看到会产生变量提升的变量都被放到了变量环境中去了。

注意词法环境组件和变量环境组件都指定了一个词法环境对象

在JavaScript中,每个运行的函数,代码块以及整个脚本,都有一个被称为词法环境(Lexical Environment)的内部(隐藏)的关联对象。

词法环境对象由两部分组成:

  1. 环境记录(Environment Record): 一个存储所有局部变量作为其属性的对象(包括一些其他信息,例如this的值)。
  2. 外部词法环境的引用,与外部代码相关联。

什么时候会创建执行上下文

  1. 全局执行上下文
    • 当JavaScript执行全局代码的时候,会编译全局代码并创建全局执行上下文,而且在整个页面的生存周期内,全局执行上下文只有一份。
  2. 函数执行上下文
    • 当调用一个函数的时候,函数体内的代码会被编译,并创建函数执行上下文,一般情况下,函数执行结束之后,创建的函数执行上下文会被销毁。
  3. eval函数
    • 当使用eval函数的时候,eval的代码也会被编译,并创建执行上下文。

只有在代码运行的时候才会产生执行上下文。

什么是执行上下文栈

首先要理解什么是栈,栈是一种后进先出的数据结构。

运行全局代码的时候,会产生全局执行上下文;当调用一个函数的时候,会创建一个函数执行上下文,那么JavaScript引擎是如何管理这么多执行上下文的呢?没错,就是通过栈这种数据结构,通常称为调用栈

当JavaScript运行一段代码时,会先将全局执行上下文压入栈底,当调用一个函数的时候,会将创建的执行上下文压入调用栈中,当函数执行完成,会将该执行上下文从调用栈中弹出。