# 执行上下文(Execution Context)

# 什么是执行上下文?

代码在运行的时候,执行的环境

# 不用时期的不同执行上下文

# 执行上下文在 ES3 中,包含三个部分

  • scope:作用域,也常常被叫做作用域链。

  • variable object:变量对象,用于存储变量的对象。

  • this value:this 值。

JavaScript 深入之执行上下文 (opens new window) 📚 高性能 JavaScript (opens new window)

# 在 ES5 中,我们改进了命名方式,把执行上下文最初的三个部分改为下面这个样子

  • lexical environment:词法环境,当获取变量时使用。

  • variable environment:变量环境,当声明变量时使用。

  • this value:this 值。

[译] 理解 JavaScript 中的执行上下文和执行栈 (opens new window)

# 在 ES2018 中,执行上下文又变成了这个样子,this 值被归入 lexical environment,但是增加了不少内容

  • lexical environment:词法环境,当获取变量或者 this 值时使用。
  • variable environment:变量环境,当声明变量时使用
  • code evaluation state:用于恢复代码执行位置。
  • Function:执行的任务是函数时使用,表示正在被执行的函数。
  • ScriptOrModule:执行的任务是脚本或者模块时使用,表示正在被执行的代码。
  • Realm:使用的基础库和内置对象实例。
  • Generator:仅生成器上下文有这个属性,表示当前生成器。

ES2018 最新 【译】理解 Javascript 中的执行上下文和执行栈 (opens new window)

# 执行上下文类型

  • 全局执行上下文

    — 这是默认或者说基础的上下文,任何不在函数内部的代码都在全局上下文中。它会执行两件事:创建一个全局的 window 对象(浏览器的情况下),并且设置 this 的值等于这个全局对象。一个程序中只会有一个全局执行上下文

  • 函数执行上下文

    每当一个函数被调用时, 都会为该函数创建一个新的上下文。每个函数都有它自己的执行上下文,不过是在函数被调用时创建的。函数上下文可以有任意多个。每当一个新的执行上下文被创建,它会按定义的顺序(将在后文讨论)执行一系列步骤。

  • Eval 函数执行上下文

    执行在 eval 函数内部的代码也会有它属于自己的执行上下文,但由于 JavaScript 开发者并不经常使用 eval,所以在这里我不会讨论它。

# 执行栈

执行栈,也就是在其它编程语言中所说的“调用栈”,是一种拥有 LIFO(后进先出)数据结构的栈,被用来存储代码运行时创建的所有执行上下文

# 对于每个执行上下文,都有三个重要属性

  • 变量对象 (variable object)
  • 作用域链 (scope chain)
  • this

# this

  • 在全局执行上下文中,this 的值指向全局对象。(在浏览器中,this 引用 Window 对象)。
  • 在函数执行上下文中,this 的值取决于该函数是如何被调用的。如果它被一个引用对象调用,那么 this 会被设置成那个对象,否则 this 的值被设置为全局对象或者 undefined(在严格模式下)。

# 变量对象

变量对象是与执行上下文相关的数据作用域,存储了在上下文中定义的变量和函数声明。

!!!全局上下文中的变量对象就是全局对象

# 执行过程 变量对象的过程

  1. 进入执行上下文 当进入执行上下文时,这时候还没有执行代码
    1. 函数的所有形参 (如果是函数上下文)
    2. 函数声明
    3. 变量声明
  2. 代码执行 在代码执行阶段,会顺序执行代码,根据代码,修改变量对象的值

总结

  1. 全局上下文的变量对象初始化是全局对象

  2. 函数上下文的变量对象初始化只包括 Arguments 对象

  3. 在进入执行上下文时会给变量对象添加形参、函数声明、变量声明等初始的属性值

4.在代码执行阶段,会再次修改变量对象的属性值

# 变量对象(VO)和活动对象(AO)的区别

未进入执行阶段之前,变量对象(VO)中的属性都不能访问!但是进入执行阶段之后,变量对象(VO)转变为了活动对象(AO),里面的属性都能被访问了,然后开始进行执行阶段的操作。它们其实都是同一个对象,只是处于执行上下文的不同生命周期。

# TDZ(暂时性死区)

块级作用域中:使用了 let 或者 const,但在还未定义式前不能被使用

if (true) {
  // TDZ开始
  tmp = 'abc' // ReferenceError
  console.log(tmp) // ReferenceError

  let tmp // TDZ结束
  console.log(tmp) // undefined

  tmp = 123
  console.log(tmp) // 123
}

# hoisting

var 在执行环境创建阶段就提升到了最顶层,在变量对象被赋值为 undefined

# 参考文章