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

后端 未结 1 1244
被撕碎了的回忆
被撕碎了的回忆 2020-12-28 10:45

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

1条回答
  •  被撕碎了的回忆
    2020-12-28 11:30

    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.

    
    

    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.

    0 讨论(0)
提交回复
热议问题