Closures and ES2015

2019-02-19 07:56发布

问题:

I apologize in advance for yet another closure question, but I'd like to clarify my understanding of the way closures are implemented in JavaScript.

Consider the following code:

01 'use strict';
02 function foo() {}
03 foo();

I have established in a question earlier in the year that a closure is conceptually (if not actually due to engine optimizations) created here.

And until foo is invoked on line 3 a corresponding execution context is not created.

So as far as I can tell from the spec, when evaluating this code:

  1. Each execution context has a "LexicalEnvironment" component used to resolve identifier references made by code within it (8.3, Table 23).
  2. FunctionCreate is called, passing in a reference (named "scope") to the "LexicalEnvironment" component of the current execution context. (14.1.19)
  3. FunctionCreate calls FunctionInitialize passing "scope" (9.2.5)
  4. FunctionInitialize ensures that the [[Environment]] internal slot of the function-object being created is set to the value of "scope" (the reference to the "LexicalEnvironment" component of the current execution context) (9.2.4)

Finally, when foo is actually invoked, I find the spec harder to interpret.

  1. In PrepareForOrdinaryCall, the "LexicalEnvironment" of the new execution context for the call is set to be the result of calling NewFunctionEnvironment (9.2.1.1)
  2. NewFunctionEnvironment copies the reference to the "LexicalEnvironment" component of the outer execution context (the [[Environment]] slot of the function-object) into the Environment record (EnvironmentRecord?) of the "LexicalEnvironment" component of the execution context under construction as the "outer lexical environment reference" (8.1.2.4)

Thus closures are implemented in a two step fashion:

  1. A linkage between function-object and the enclosing "LexicalEnvironment" of the enclosing execution context, is created at function-object instantiation time. This is the [[Environment]] internal slot of the function-object.
  2. When a function is invoked, this reference to the enclosing "LexicalEnvironment" component of the outer execution context (the contents of the [[Environment]] slot of the function object) is copied into a spec-imprecisely-defined(?)/EnvironmentRecord(?) sub-component of the "LexicalEnvironment" component of the new execution context.

Does this sound about right?

回答1:

Does this sound about right?

Pretty much. I just wouldn't use the work "copied", rather "linked". To simplify it a little:

  1. When a function is created, it stores are reference to the environment it was created it.
  2. When the function is executed, this stored environment becomes the "outer environment" of the newly created function environment.

Or in pictures:

                  +----------------+                     +----------------+
   Function       |                |   [[Environment]]   |     Outer      |
   creation       |    Function    |-------------------->|  Environment   |
                  |                |                     |                |
                  +----------------+                     +----------------+
                                                                  ^        
                                                                  |        
                                                                  |        
                  +----------------+                              |        
   Function       |    Function    |  outer environment reference |        
   execution      |  Environment   |------------------------------+        
                  |                |                                       
                  +----------------+                                       

This happens to every function and depending on your definition of closure1, this makes every function a closure (or not).


1: I believe there are these two takes on what it means for a function to be a closure:

  • If a function stores a references to the environment it was created in, it's a closure (applies to every function in JS)
  • If a function stores a reference to the environment it was created in and "leaves" that environment (the environment "ceases to exist"), it's a closure. That's of course not always the case.