JavaScript Closures - Using the ECMA Spec, please explain how the closure is created and maintained

Deadly 提交于 2019-12-18 11:43:00

问题


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


回答1:


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.



来源:https://stackoverflow.com/questions/15117687/javascript-closures-using-the-ecma-spec-please-explain-how-the-closure-is-cre

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!