JavaScript Closures - 使用ECMA Spec,请解释如何创建和维护闭包 [英] JavaScript Closures - Using the ECMA Spec, please explain how the closure is created and maintained

查看:116
本文介绍了JavaScript Closures - 使用ECMA Spec,请解释如何创建和维护闭包的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在阅读关于 JavaScript闭包的信息。我熟悉执行上下文词汇环境,并且非常熟悉词汇范围设定



我想要要了解创建维护的JavaScript中的如何闭包。有时,我很难掌握这些重要的概念,而不知道如何它实际上是做它。我知道,根据维基百科,闭包是


是一个函数或引用一个函数与引用环境 - 一个表存储对这个函数的每个非局部变量(也称为自由变量)的引用。


但我的问题是,如何知道如何,根据 ECMA规格 ,创建和维护闭包。我不是正在寻找封闭理论的高级解释,请在您的答案中参考ECMA规范。



注意:请不认为这是重复,除非答案解释了使用ECMA规范的闭包。再次,我不感兴趣的人引用维基百科和一个例子,我想完全理解JavaScript如何做到这一点。 (我熟悉 SO上的此问题)。

解决方案

根据维基百科的定义,如问题所述,闭包是


是一个函数或引用一个函数以及一个引用环境 - 存储对该函数的每个非局部变量(也称为自由变量)的引用的表。


了解如何维护执行上下文和词汇环境是已知的,此处的目标是了解何时返回函数,如何



8.6.2 在ECMA 262 v 5规范中,它列出了内部属性。这里要指出的是表9中的[[Scope]]属性。根据这个属性的描述,它被描述为


定义一个函数对象被执行的环境的词法环境。在标准的内置ECMAScript对象中,只有Function对象实现了[[Scope]]。


正如我们将看到的, Scope]]属性将始终设置为父级的词法环境。我们在 13.2 部分提到这一点创建函数对象。 (请注意:在这个上下文中的函数对象是指一个本地的ECMAScript对象,而不是通过代码访问的函数对象)。



内部[[Scope]]属性到运行执行上下文的VariableEnvironment,LexicalEnvironment或Global Environment,取决于函数是否为函数声明函数表达式,或通过创建函数构造函数



当控制被赋予全局代码时,以及当控制输入功能代码时,声明绑定实例化作为初始化执行上下文。声明绑定实例化的一部分是通过创建第13.2节中提到的函数对象来绑定当前上下文范围内的函数声明。下面的示例显示了这一点:



例如

  //全局执行上下文已经在这个阶段被初始化。 
//声明绑定实例化已经发生,函数
// foo被创建为一个带有[[Scope]]属性的新的本地对象
//具有全局执行上下文的值VariableEnvironment
function foo(){
//当调用foo时,将为
//此函数范围创建一个新的执行上下文。当声明绑定实例化发生时,
// bar将被创建为具有[[Scope]]属性的新本地对象
//具有foo执行上下文的VariableEnvironment的值
function bar (){
}
bar(); // Call bar
}
foo();另一件需要注意的事情是在进入/创建执行上下文时发生的进程 http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.3rel =nofollow>当输入函数时。


  1. 通过内部调用NewDeclarativeEnvironment创建一个新的词法环境类型。该函数的[[Scope]]属性将被设置为 outer 引用,以便维护词法环境链。 (记住,[[Scope]]属性被设置,并且将始终是父类的词法作用域。此外,词法环境链是我组成的一个短语,该概念指的是通过外部引用遍历词法环境来解析标识符,直到标识符


  2. 在步骤1中将LexicalEnvironment和VariableEnvironment设置为新创建的词汇环境。

  3. 执行声明绑定实例化

通过知道一个函数通过内部[[Scope]]属性保持对它的父词法环境的引用,我们现在可以看到闭包是如何工作的。

 < script& 
// foo。[[Scope]]在全局执行上下文初始化期间设置为全局环境
function foo(){
var x = 1;
// bar。[[Scope]]在foo的执行上下文初始化期间设置为foo的词法环境
function bar(){
var y = 2;
alert(x + y)
}
return bar;
}

var dummy = foo(); //将变量绑定dummy分配给bar函数的引用。
dummy(); //调用bar功能代码。引用仍然有它的[[Scope]]属性设置,因此标识符查找在解析标识符时按预期工作。
alert(dummy.name); // Alertsbar;

< / script>

所以为了回答这个问题,一个函数parent LexicalEnvironment通过内部[[Scope]]属性的功能。注意,当函数运行时,函数内的局部变量可以被解析,只有自由变量需要被跟踪,并由[[Scope]]属性来完成。



请注意:如果我的资讯有误,请在下方留言。


I'm reading about JavaScript closures. I'm familiar with Execution Contexts, how the Lexical Environment is maintained, and very familiar with Lexical Scoping.

I want to know how closures in JavaScript are created and maintained. Sometimes it's hard for me to grasp such important concepts without knowing how it is actually doing it. I know that, according to Wikipedia, a closure is

is a function or reference to a function together with a referencing environment—a table storing a reference to each of the non-local variables (also called free variables) of that function.

But my question is, I want to know how, according to the ECMA Specification, that a closure is created and maintained. I'm not looking for a high-level explanation of closure theory, please reference the ECMA Specification in your answer.

Note: Please do not consider this a duplicate, unless the answer explains a closure using the ECMA Specification. Again, I'm not interested in someone quoting wikipedia and giving an example, I want to fully understand how JavaScript does this. (I'm familiar with this question on SO).

解决方案

According to the Wikipedia definition, as mentioned in the question, a closure is a

is a function or reference to a function together with a referencing environment—a table storing a reference to each of the non-local variables (also called free variables) of that function.

The understanding of how execution contexts and lexical environments are maintained is known and the goal here is to understand when a function is returned, how is that referencing environment maintained/referenced?

Let's begin.

In section 8.6.2 on the ECMA 262 v 5 specification, it lists the internal properties of ECMAScript objects. The one to point out here is in table 9, the [[Scope]] property. According to the description of this property, it is described as

A lexical environment that defines the environment in which a Function object is executed. Of the standard built-in ECMAScript objects, only Function objects implement [[Scope]].

As we will see, the [[Scope]] property of a function object will always be set to the parent's lexical environment. We see this mentioned in section 13.2 which talks about the process of creating a function object. (Please note: function object in this context is referring to a native ECMAScript object, and not the accessible function object through code).

When a function is created, it sets the internal [[Scope]] property to the VariableEnvironment, LexicalEnvironment or Global Environment of the running execution context, depending on if the function is a function declaration, function expression or created through the Function constructor.

When control is given over to the global code, as well as when control enters function code, declaration binding instantiation occurs as part of initializing the execution context. Part of the declaration binding instantiation is to bind the function declarations within the scope of the current context by creating the function objects as mentioned in section 13.2. The below example shows this:

For example

  // 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();

Another thing to look is the process that occurs upon entering/creating the execution context when entering a function. Below is a summary of what happens.

  1. Create a new Lexical Environment type by internally calling NewDeclarativeEnvironment. The [[Scope]] property of the function will be set as the outer reference in order for the "Lexical Environment" chain to be maintained. (Remember that the [[Scope]] property was set and will always be the parent's lexical scope. Also, Lexical Environment chain is a phrase I made up, the concept referring to resolving identifiers by traversing the Lexical Environments through outer references until the identifier can be resolved.)
  2. Set the LexicalEnvironment and VariableEnvironment to the newly created Lexical Environment in step 1.
  3. Perform declaration binding instantiation.

With the knowledge that a function maintains a reference to it's parent Lexical Environment through the internal [[Scope]] property, we can now see how closures work.

<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>

So to answer the question, a functions parent LexicalEnvironment is persisted through the internal [[Scope]] property of the function. Note that local variables within the function can be resolved when the function is run, only "free variables" need to be tracked and are done so by the [[Scope]] property.

Note: Please, if my information is incorrect, comment below.

这篇关于JavaScript Closures - 使用ECMA Spec,请解释如何创建和维护闭包的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆