con*_*att 5 javascript closures ecma262 ecmascript-5
我正在阅读有关JavaScript闭包的内容.我熟悉执行上下文,如何维护词汇环境,以及非常熟悉词法范围.
我想知道如何创建和维护 JavaScript中的闭包.有时我很难掌握这些重要的概念而不知道它是如何实现的.我知道,根据维基百科的说法,关闭是
是函数或函数的引用以及引用环境 - 一个存储对该函数的每个非局部变量(也称为自由变量)的引用的表.
但我的问题是,根据ECMA规范,我想知道如何创建和维护闭包.我不是在寻找关闭理论的高级解释,请在答案中参考ECMA规范.
注意:请不要认为这是重复的,除非答案解释了使用ECMA规范的闭包.同样,我对引用维基百科和举例的人不感兴趣,我想完全理解JavaScript如何做到这一点.(我在SO上熟悉这个问题).
根据维基百科的定义,如问题所述,闭包是一个
是函数或函数的引用以及引用环境 - 一个存储对该函数的每个非局部变量(也称为自由变量)的引用的表.
对如何维护执行上下文和词汇环境的理解是已知的,这里的目标是了解何时返回函数,该引用环境是如何维护/引用的?
让我们开始.
在ECMA 262 v 5规范的8.6.2节中,它列出了ECMAScript对象的内部属性.这里要指出的是表9中的[[Scope]]属性.根据该属性的描述,将其描述为
一种词法环境,用于定义执行Function对象的环境.在标准的内置ECMAScript对象中,只有Function对象实现[[Scope]].
正如我们将看到的,函数对象的[[Scope]]属性将始终设置为父级的词法环境.我们在第13.2节中看到了这一点,它讨论了创建函数对象的过程.(请注意:此上下文中的函数对象是指本机ECMAScript对象,而不是通过代码访问的函数对象).
创建函数时,它将内部[[Scope]]属性设置为正在运行的执行上下文的VariableEnvironment,LexicalEnvironment或Global Environment,具体取决于函数是函数声明,函数表达式还是通过Function构造函数创建的.
当控制权交给全局代码时,以及当控制进入功能代码时,声明绑定实例化作为初始化执行上下文的一部分发生.声明绑定实例化的一部分是通过创建第13.2节中提到的函数对象来绑定当前上下文范围内的函数声明.以下示例显示了这一点:
例如
// The global execution context has already been initialized at this stage.
// Declaration binding instantiation has occurred and the function
// foo is created as a new native object with a [[Scope]] property
// having the value of the global execution context's VariableEnvironment
function foo() {
// When foo is invoked, a new execution context will be created for
// this function scope. When declaration binding instantiation occurs,
// bar will be created as a new native object with a [[Scope]] property
// having the value of the foo execution context's VariableEnvironment
function bar() {
}
bar(); // Call bar
}
foo();
Run Code Online (Sandbox Code Playgroud)
另一个需要注意的是在输入函数时输入/创建执行上下文时发生的过程.以下是所发生情况的摘要.
由于知道函数通过内部[[Scope]]属性维护对其父级词汇环境的引用,现在我们可以看到闭包是如何工作的.
<script>
// foo.[[Scope]] was set to the global environment during the global execution context initialization
function foo() {
var x = 1;
// bar.[[Scope]] was set to foo's lexical environment during foo's execution context initialization
function bar() {
var y = 2;
alert(x + y);
}
return bar;
}
var dummy = foo(); // Assign variable binding "dummy" to a reference of the "bar" function.
dummy(); // Calls the "bar" function code. The reference still has it's [[Scope]] property set, thus the identifier look-ups work as expected when resolving the identifiers.
alert(dummy.name); // Alerts "bar";
</script>
Run Code Online (Sandbox Code Playgroud)
因此,为了回答这个问题,函数父LexicalEnvironment通过函数的内部[[Scope]]属性保持不变.注意,在运行函数时,可以解析函数中的局部变量,只需要跟踪"自由变量",并通过[[Scope]]属性完成.
注意: 如果我的信息不正确,请在下方发表评论.