Skip to content

this 的本质

执行上下文(Execution Context)到底是什么?[[LexicalEnvironment]]this 是怎么共存的?

一、执行上下文的本质:一个抽象记录(Abstract Record)

根据 ECMA-262 §8.3, 一个执行上下文在规范中被定义为一个包含三个字段的抽象记录

text
ExecutionContext = {
  [[Function]]: Function | null,           // 当前正在执行的函数(如果是函数调用)
  [[Realm]]: Realm,                        // 当前代码运行的环境(全局对象、内置对象等)
  [[ScriptOrModule]]: Script | Module | null,
  [[LexicalEnvironment]]: Lexical Environment,     // 👈 词法环境
  [[VariableEnvironment]]: Lexical Environment,    // 👈 变量环境
  [[ThisBinding]]: any                     // 👈 this 的值
}

🔥 所以你完全正确:执行上下文的核心字段之一就是 [[LexicalEnvironment]]

但这不是说“执行上下文是 [[LexicalEnvironment]]”,而是: 执行上下文“拥有”一个 [[LexicalEnvironment]] 字段,用来管理当前作用域中的变量声明。

二、this 在哪?就在 [[ThisBinding]] 里!

你问得好:“那 this 在这上面吗?”

答案是:在!它就是 [[ThisBinding]]

内部槽含义
[[LexicalEnvironment]]当前作用域中 letconstclass 等的绑定环境
[[VariableEnvironment]]当前作用域中 var、函数声明的绑定环境(通常和 [[LexicalEnvironment]] 初始时相同)
[[ThisBinding]]当前上下文中 this 的值

所以:this 不是藏在 [[LexicalEnvironment]] 里,而是和它并列的一个独立字段。

三、举个例子:函数执行时发生了什么?

js
const obj = {
  name: "Alice",
  greet() {
    let a = 1;
    var b = 2;
    console.log(this.name); // Alice
  }
};

obj.greet();

greet() 执行时,引擎创建一个函数执行上下文

text
{
  [[Function]]: greet,
  [[Realm]]: 当前全局环境,
  [[ScriptOrModule]]: ...,
  [[LexicalEnvironment]]: {
    // 环境记录(Environment Record):
    type: 'object-environment' or 'declarative',
    bindings: { a: 1 }        // let/const/class
  },
  [[VariableEnvironment]]: {
    bindings: { b: 2 }        // var/function declarations
  },
  [[ThisBinding]]: obj         // 👈 this 就是 obj
}

所以 this.name 能访问到 "Alice",是因为:

  1. this[[ThisBinding]] 的值(obj
  2. obj.name 是对象属性,直接查找

ab 是通过 [[LexicalEnvironment]][[VariableEnvironment]] 查找的。

四、[[ThisBinding]] 是怎么设置的?

它在执行上下文创建阶段根据调用方式确定:

调用方式[[ThisBinding]] 设置为
obj.method()obj
fn()全局对象(非严格) / undefined(严格)
new Fn()新创建的实例
fn.call(ctx)ctx
箭头函数继承外层上下文的 [[ThisBinding]]

注意:箭头函数没有自己的 [[ThisBinding]],它直接复用外层的。

五、图解:执行上下文的完整结构

执行上下文 (ExecutionContext)

├── [[Function]]       → 指向当前执行的函数
├── [[Realm]]          → 运行环境(内置对象、全局对象等)
├── [[ScriptOrModule]] → 当前执行的是脚本还是模块
├── [[LexicalEnvironment]] 
│     └── 环境记录
│         ├── a: 1      // let/const
│         └── inner: function

├── [[VariableEnvironment]]
│     └── 环境记录
│         └── b: 2      // var

└── [[ThisBinding]]     → obj(或 window, undefined, 实例等)

你看,[[LexicalEnvironment]][[ThisBinding]] 是“兄弟关系”,不是“父子关系”。

六、为什么 this 不放在 [[LexicalEnvironment]] 里?

这是个极好的问题!

原因如下:

  1. 语义分离

    • [[LexicalEnvironment]] 管理的是标识符绑定(变量名 → 值)
    • this 不是一个变量名,而是一个执行上下文的状态
    • 它更像是“当前上下文的配置项”,而不是“可声明的标识符”
  2. 查找机制不同

    • 变量查找:沿着作用域链([[Environment]] 链)向上找
    • this 查找:不走作用域链!它只由调用方式决定
      js
      function foo() {
        console.log(this); // 不是从外层 scope 找来的!
      }
  3. 性能优化

    • this 是高频访问的值,引擎可以专门优化 [[ThisBinding]] 的读取
    • 如果混在环境记录里,查找效率会下降