我已经阅读了几篇关于执行上下文的综合文章,但现在我有点困惑和混乱。
为了尽可能简短地提出问题,避免长篇引用,我最好通过一个例子来说明我的心理模型,并重点关注我无法理解的细节,以便您可以纠正我并指出错误。
以下是一个例子:
var tomato = 'global tomato';
{
let tomato = 'block tomato';
console.log(tomato); // 'block tomato'
}
console.log(tomato); // 'global tomato'
到目前为止,所有的都很清楚。当JS引擎创建执行上下文(在我们这个例子中是全局上下文)时,来自第一行的
var tomato
声明被放置在一个变量环境
中,而块级作用域中的let tomato
则进入了词法环境
。这就解释了为什么我们最终得到了两个不同的tomatoes。现在,让我们再添加另一个tomato,就像这样:
var tomato = 'global tomato';
{
let tomato = 'block tomato';
{
console.log(tomato); // ReferenceError: Cannot access 'tomato' before initialization
let tomato = 'nested block tomato';
}
console.log(tomato); // won't reach here
}
console.log(tomato); // won't reach here
ReferenceError
不足为奇。事实上,我们试图在变量初始化之前访问它,这被称为暂时性死区。这很好地说明了JS已经在最嵌套的块中创建了另一个变量tomato
。此外,JS已经意识到当前引用的tomato
未被初始化。否则,它将从等于 'block tomato'
的外部作用域中获取tomato
而不会抛出任何错误。因此,让我们修复错误并交换这两行代码,像这样:
var tomato = 'global tomato';
{
let tomato = 'block tomato';
{
let tomato = 'nested block tomato';
console.log(tomato); // 'nested block tomato'
}
console.log(tomato); // 'block tomato' - still 'block tomato'. Nothing has been overwritten.
}
console.log(tomato); // 'global tomato'
我想知道的是JavaScript是如何管理最嵌套的代码块的。因为执行到以下这行代码时:
执行上下文执行时,变量的
let tomato = 'nested block tomato';
Lexical Environment
已经包含在外部范围初始化的变量tomato
并赋值为'block tomato'
。假设JS不会为代码块创建新的执行上下文(具有各自的词法和变量环境),除了函数调用和全局脚本之外(是这样吧?),显然它不会覆盖现有的Lexical Environment
中同名但来自嵌套块作用域的变量。就像最后一段代码所示,创建了一个全新、独立的变量来存储值'nested block tomato'
。那么问题来了,这个变量究竟存储在哪里呢?我的意思是,一个执行上下文只有一个
Lexical Environment ,但我们可能会在其中创建 numerous 个声明变量的嵌套作用域。我很难想象这些变量将被存储在哪里,以及整个东西如何拼合在一起。