tl;博士
它们都是Environment Record
.
LexicalEnvironment
只是执行上下文的一个组件(函数不能有 a LexicalEnvironment
),并且在调用函数时,LexicalEnvironment
会为当前上下文创建一个新的,其[[OuterEnv]]
字段被设置为 Function[[Environment]]
字段。
如果是 Javascript,我想它会是:
function handleInvokeFunction(func) {
const localEnv = new EnvironmentRecord();
localEnv.set('[[OuterEnv]]', func['[[Environment]]'])
calleeContext.lexicalEnvironment = localEnv;
}
免责声明:我不是该主题的专家。我只是想给你一个整体的想法,同时等待真正的专家在这里插话。
环境记录
Environment Records用于记录(双关语),包含要执行的功能所需的所有信息。例如,对于函数,它们包含变量声明和this
值。当然,这过于简单了[src]。
环境记录是一种规范类型,用于定义标识符与特定变量和函数的关联。
每次评估此类代码时,都会创建一个新的环境记录来记录由该代码创建的标识符绑定。
关于环境记录的一件有趣的事情是,它们负责允许访问父变量,例如:
// Environment Record "EnvA"
const hello = "world";
if (1) {
// Environment Record "EnvB"
console.log(hello);
}
// outputs: world
那是因为它们有一个名为 的字段[[OuterEnv]]
,它指向父环境。所以在上面的例子中, "EnvB" 的[[OuterEnv]]
字段被设置为 "EnvA" [src]。
每个环境记录都有一个[[OuterEnv]]
字段,该字段为空或对外部环境记录的引用。
每次运行时遇到新的代码块时,它都会执行以下步骤[src]:
- 创建一个新的环境记录。
- 将该新环境的字段设置
[[OuterEnv]]
为旧的(当前活动的)环境。
- 回归新环境
执行上下文
为了对所有块执行此操作,使用了执行上下文堆栈,这几乎就像堆栈跟踪 [src]。不同之处在于,它不仅仅在进入和退出函数时推送和弹出(就像堆栈跟踪那样),它只会在进入或退出代码块(如 if 块)时更改最顶层的条目。
执行上下文是一种规范设备,用于跟踪 ECMAScript 实现对代码的运行时评估。
执行上下文堆栈用于跟踪执行上下文。
执行上下文有一个LexicalEnvironment
组件。需要跟踪该特定代码块中的变量。
LexicalEnvironment:标识用于解析此执行上下文中的代码所做的标识符引用的环境记录。[源]
LexicalEnvironment
是一个环境记录,所以它有一个[[OuterEnv]]
字段,它是运行时将相应更改的内容。
LexicalEnvironment
不属于函数对象。它只属于一个执行上下文。
运行执行上下文表示运行时当前正在执行的代码块[ src]。
正在运行的执行上下文始终是此堆栈的顶部元素。
为了扩展上述步骤,当输入一个新的代码块时,这就是实际发生的情况[src]:
- 创建具有适当
[[OuterEnv]]
值的新环境记录(与之前的步骤相同)。
- 使用新的环境记录作为运行记录。
- 评估块内的所有行。
- 恢复到以前的环境记录。
- 返回结果并退出块。
评论前面的例子,这就是会发生的事情:
// This is Environment Record "EnvA".
// The [[OuterEnv]] field for "EnvA" is null.
// The running context LexicalEnvironment is "EnvA".
const hello = "world";
if (1) {
// Found new block
// Create a new Environment Record "EnvB".
// Set the "EnvB" [[OuterEnv]] field to
// the running context LexicalEnvironment.
// In this case, its "EnvA".
// Change the running context LexicalEnvironment to "EnvB".
// Evaluate all lines in the body using the new
// running context LexicalEnvironment.
// In this case, its "EnvB".
console.log(hello);
// Restore the previous running context LexicalEnvironment.
// Return the result.
}
// The running context LexicalEnvironment is Environment Record "A".
// Since the inner block restored before returning, it didn't change.
[[环境]]
不过,还没有提到功能。这是不同的,因为函数可以在声明的范围之外执行。
那就是[[Environment]]
出现的地方。
[[Environment]]:关闭函数的环境记录。在评估函数的代码时用作外部环境。
当块内有函数时,将运行LexicalEnvironment
存储为[[Environment]]
函数对象的字段[步骤35] [步骤3] [步骤14]。
调用该函数时,该字段[[Environment]]
用作[[OuterEnv]]
[step 10]。
这就像该函数将它可以访问的所有变量存储在 中[[Environment]]
,并且在调用时,它可以再次使用[[Environment]]
.
与普通块的另一个区别是,在这种情况下,不是更改正在运行的执行上下文,而是创建一个新的并将其推送到堆栈[步骤 3 中的创建] [步骤 12 中的推送] [步骤 8 中的弹出]。
现在,尝试使用简单的代码:
// This is Environment Record "EnvA".
// The [[OuterEnv]] field for "EnvA" is null.
// The running context LexicalEnvironment is "EnvA".
const hello = "world";
// Found a function, store the running context
// into its [[Environment]] field, and do nothing else.
function foo() {
// This block runs only after invoking bar().
// Create a new executing context "calleeContext".
// Create a new Environment Record "EnvB".
// Set the "EnvB" [[OuterEnv]] field, to the value
// stored inside [[Environment]]. In this case, its "EnvA".
// Set the LexicalEnvironment of "calleeContext" to "EnvB".
// Push "calleeContext" to the execution context stack.
// That makes "calleeContext" the running execution context.
// Evaluate all lines in the body
// using "calleeContext" LexicalEnvironment.
// In this case, its "EnvB".
// If a function is found here, set its
// [[Environment]] to "calleeContext" LexicalEnvironment.
console.log(hello); // works because `hello` was in "EnvA"
// Pop "calleeContext" from the execution context stack.
// "calleeContext" is no longer the running execution context.
// Return the result.
}
const bar = foo;
bar();
// The [[Environment]] of `bar` is still "EnvA".
// The running context LexicalEnvironment is still "EnvA".
由于该示例在声明它的同一环境中调用该函数,因此它实际上并没有使用“闭包”,但您可能已经明白了。
综上所述
虽然两者[[Environment]]
和LexicalEnvironment
都是Environment Records
,但它们用于不同的事物。
[[Environment]]
保存LexicalEnvironment
函数声明的位置。
LexicalEnvironment
是执行上下文的一个组件,它存储有关该特定代码块中变量的信息。