You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于LexicalEnvironment与VariableEnvironment的理解是否正确?

LexicalEnvironment与VariableEnvironment的核心区别及执行流程解析

先明确ECMAScript规范的核心规则:

  • 执行上下文创建时,LexicalEnvironment(LE)和VariableEnvironment(VE)初始指向同一个词法环境。这个初始环境会完成var声明的变量提升(值为undefined)和函数声明的提升。
  • VE从始至终固定指向这个初始环境,仅用于存储var和函数声明的绑定。
  • LE则是动态变化的:每当进入一个块级作用域(函数体、if/while块、let/const所在的块等),都会创建一个新的词法环境,将其outer指向当前的LE,然后把LE更新为这个新环境。新环境专门存储当前块内的let/const声明(处于未初始化状态,形成暂时性死区);离开块级作用域时,LE会回退到之前的环境。

针对你提供的代码,正确的执行流程应该是这样:

代码示例:

function do_something() {
    var a = 1;
    let b = 2;
    while (true) {
        var c = 3;
        let d = 4;
        console.log(b);
        break;
    }
}
do_something();

1. 执行do_something(),创建函数执行上下文

此时LE和VE指向同一个初始词法环境,完成var声明的提升:

ExecutionContext:
    LexicalEnvironment:
        a -> undefined, c -> undefined
        outer: global
    VariableEnvironment:
        a -> undefined, c -> undefined
        outer: global
    ...

2. 进入函数体块级作用域

创建新的词法环境,将LE更新为这个新环境(其outer指向VE),并在其中添加let b的绑定(未初始化):

ExecutionContext:
    LexicalEnvironment:
        b -> uninitialized
        outer: VariableEnvironment // 指向初始环境
    VariableEnvironment:
        a -> undefined, c -> undefined
        outer: global
    ...

执行var a = 1:VE中的a被赋值为1;执行let b = 2:LE中的b被赋值为2。

3. 进入while块级作用域

创建新的词法环境,LE更新为这个新环境(其outer指向当前的函数体LE),添加let d的绑定(未初始化):

ExecutionContext:
    LexicalEnvironment:
        d -> uninitialized
        outer: 
            LexicalEnvironment(函数体)
                b -> 2
                outer: VariableEnvironment
    VariableEnvironment:
        a -> 1, c -> undefined
        outer: global
    ...

执行var c = 3:VE中的c被赋值为3;执行let d = 4:当前LE中的d被赋值为4;console.log(b)时,从当前LE向上查找,找到函数体LE中的b,输出2。

4. 离开while块

LE回退到函数体的词法环境:

ExecutionContext:
    LexicalEnvironment:
        b -> 2
        outer: VariableEnvironment
    VariableEnvironment:
        a -> 1, c -> 3
        outer: global
    ...

关于你的模型的修正点

  • 离开while块后,b不会变成nothing,它依然存在于函数体的LE中,值为2,直到函数执行完毕。
  • 不是“遇到let/const时创建新LE”,而是进入块级作用域时创建新LE,let/const只是在当前块的LE中添加绑定。

闭包场景的解释

闭包的核心是函数的[[Environment]]属性会保留其创建时所在的LE引用。比如修改代码:

function do_something() {
    let b = 2;
    return function() {
        console.log(b);
    }
}
const fn = do_something();
fn(); // 输出2

当do_something执行时,返回的内部函数的[[Environment]]指向函数体的LE(包含b -> 2,outer指向VE)。即使do_something执行完毕,这个LE不会被垃圾回收,因为内部函数还在引用它。当调用fn()时,会创建新的执行上下文,其LE的outer指向这个保留的环境,从而能找到b的值,形成闭包。

内容的提问来源于stack exchange,提问作者1_sy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 20:23:37