执行上下文与作用域

SHEINBLOOMCHIC
执行上下文 (Execution Context Stack)也可称作函数调用栈,函数执行栈。
作用域是用来限制一个变量在程序中使用的范围。
执行上下文在运行时确定,作用域在定义时就已经确定。

js中,函数被执行的过程分成两个部分:

  1. 生成执行上下文环境,确定this指向,声明变量,生成作用域链。此时函数还没有真正执行,这一步的作用是生成函数的运行环境。
  2. 按顺序逐行执行代码。这一步是真正的执行函数。
  • 生成执行上下文(函数被调用时,函数真正执行前):
    1. 生成变量对象(当前函数上下文可访问的变量,变量提升发生在这个阶段)。顺序:创建arguments,创建function函数声明,创建var变量声明。
    2. 生成作用域链。
    3. 确定this指向。
  • 函数执行阶段
    逐行执行代码,这个阶段会完成变量赋值,函数引用,执行其他代码等。

执行上下文

js中有三种执行上下文:

  • 全局执行上下文。任何不在函数内的代码都在全局上下文中。全局上下文会创建一个全局的window/global对象,并且设置this的值等于这个全局对象。一个程序中只会有一个全局执行上下文。
  • 函数执行上下文。每个函数被调用时,都会创建一个自己的执行上下文。每个函数的执行上下文是在自身被调用的时候创建的。函数上下文可以有任意多个。
  • Eval函数执行上下文。执行在eval函数内的代码有自己的执行上下文。但是不经常用。

执行上下文生命周期包括:创建阶段 -> 执行阶段 -> 回收阶段

创建阶段

此时函数未被真正执行。这个阶段中

  1. 绑定this值指向。
  2. 创建词法环境(LexicalEnvironment component)
  3. 创建变量环境(VariableEnvironment component)
ExecutionContext = { // 执行上下文(函数调用栈) ThisBinding = <this value>, // this绑定 LexicalEnvironment = {...}, // 词法环境 VariableEnvironment = {...}, // 变量环境 }

this值取决于当前函数被谁调用。

词法环境

词法环境指代码块内标识符与变量值、函数值之间的关联关系

  • 全局环境
  • 函数环境

词法环境内部有两个组件:

  • 环境记录器:存储变量和函数声明的实际位置
    其内包含两种类型:
    • 声明式环境记录器(在函数环境中):存储变量、函数和参数(arguments对象和传递给函数的参数的length)。
    • 对象环境记录器(在全局环境中):用来定义出现在全局上下文中的变量和环境的关系。
GlobalExecutionContext = { // 全局执行上下文 LexicalEnvironment: { // 词法环境 EnvironmentRecord: { // 环境记录器:存储变量和函数声明的实际位置 Type: "Object", // 绑定标识符 }, outer: <null> // 对外部环境的引用:可以访问其父级词法环境 } } FunctionExecutionContext = { // 函数执行上下文 LexicalEnvironment: { // 词法环境 EnvironmentRecord: { Type: "Declarative", // 绑定标识符 }, outer: <Global or outer function environment reeference> } }

变量环境

变量环境也是一个词法环境,有着和上面的词法环境同样的属性。词法环境和变量环境不同之处是词法环境用来存储函数声明和let const绑定,变量环境用来存储 var绑定

经历以上两个阶段后,通过var定义的变量初始值为undefined,通过letconst定义的变量为uninitialized(未初始化)状态,因此不能在letconst变量声明前就访问。也就是我们常说的变量提升。同名的functionvar,由于function先被解析,因此会被同名var覆盖。

执行阶段

变量赋值、代码执行

回收阶段

执行上下文出栈,等待虚拟机回收

作用域链

当函数在解析时,向上查找变量的过程,就是作用域链的查找过程。作用域链是执行上下文中的一部分。

评论加载中 (ง •̀ω•́)ง