据说每个代码块都有一个名为 LexicalEnviroment
的隐藏对象。 .该对象包含对外部作用域的引用和 EnviromentRecord
,其中包含有关当前作用域的信息。
另一方面,据说函数能够闭包,这要归功于 [[Enviroment]]
构造“记住函数的定义位置”。
搞糊涂了,LexicalEnviroment
有什么关系对象和 [[Enviroment]]
?
他们是一回事吗?只有函数有[[Enviroment]]
构造?他们有 LexicalEnviroment
对象呢?
最佳答案
tl;博士
它们都是 Environment Record
的实例.LexicalEnvironment
只是执行上下文的一个组件(函数不能有 LexicalEnvironment
),并且在调用函数时,一个新的 LexicalEnvironment
为当前上下文创建,其 [[OuterEnv]]
字段被设置为函数 [[Environment]]
field 。
如果是 Javascript,我想应该是:
function handleInvokeFunction(func) {
const localEnv = new EnvironmentRecord();
localEnv.set('[[OuterEnv]]', func['[[Environment]]'])
calleeContext.lexicalEnvironment = localEnv;
}
免责声明:我不是该主题的专家。我只是想给你一个整体的想法,同时等待真正的专家在这里插话。
环境记录
Environment Records ,为了记录(双关语),保存要执行的功能所需的所有信息。例如,对于函数,它们保存变量声明,而
this
值(value)。当然,这过于简单化[src] .Environment Record is a specification type used to define the association of Identifiers to specific variables and functions.
Each time such code is evaluated, a new Environment Record is created to record the identifier bindings that are created by that code.
关于环境记录的一件有趣的事情是,它们负责允许访问父变量,例如:
// Environment Record "EnvA"
const hello = "world";
if (1) {
// Environment Record "EnvB"
console.log(hello);
}
// outputs: world
那是因为他们有一个名为 [[OuterEnv]]
的字段。 ,它指向父环境。所以在上面的例子中,[[OuterEnv]]
“EnvB”字段设置为“EnvA”[src] .Every Environment Record has an
[[OuterEnv]]
field, which is either null or a reference to an outer Environment Record.
每次运行时遇到新的代码块时,它都会执行以下步骤 [src] :
[[OuterEnv]]
新环境到旧(当前事件)环境的领域。 执行上下文
为了对所有块执行此操作,执行上下文堆栈 使用,这几乎就像一个 堆栈跟踪 [src] .不同之处在于,它不仅仅是在进入和退出 时推送和弹出。功能 (就像堆栈跟踪一样),它只会在进入或退出 时更改最上面的条目。代码块 (就像一个 if 块)。
An execution context is a specification device that is used to track the runtime evaluation of code by an ECMAScript implementation.
The execution context stack is used to track execution contexts.
执行上下文有一个
LexicalEnvironment
成分。需要跟踪该特定代码块中的变量。LexicalEnvironment: Identifies the Environment Record used to resolve identifier references made by code within this execution context. [src]
LexicalEnvironment
是 一个环境记录,所以它有一个 [[OuterEnv]]
字段,这是运行时将相应更改的内容。LexicalEnvironment
不属于函数对象。它只属于一个执行上下文。正在运行 执行上下文表示当时运行时当前正在执行的代码块 [src] .
The running execution context is always the top element of this stack.
扩展上述步骤,当输入新的代码块时,实际会发生这种情况 [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]]: The Environment Record that the function was closed over. Used as the outer environment when evaluating the code of the function.
当块内有函数时,运行
LexicalEnvironment
存储为 [[Environment]]
函数对象的字段[step 35] [step 3] [step 14] .调用该函数时,
[[Environment]]
字段用作 [[OuterEnv]]
[step 10] .这就像函数将所有它可以访问的变量存储在
[[Environment]]
中。 ,当被调用时,它可以使用 [[Environment]]
再次访问它们。 .与普通块的另一个区别是,在这种情况下,不是更改 正在运行 执行上下文,一个新的被创建并推送到堆栈 [creation in step 3] [push in step 12] [pop in step 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
是执行上下文的一个组件,它存储有关该特定代码块中变量的信息。
关于javascript - LexicalEnviroment 对象和 [[Environment]] 之间的关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64202695/