JavaScript closure 2: Implementation of closure

Source: Internet
Author: User
Tags try catch
After discussing the theory, let's introduce how closure is implemented in ECMAScript.

After discussing the theory, let's introduce how closure is implemented in ECMAScript. It is necessary to emphasize that ECMAScript only uses static (lexical) scopes (while languages such as Perl can use static scopes or dynamic scopes for variable Declaration ).

Var x = 10; function foo () {alert (x) ;}( function (funArg) {var x = 20; // variable "x" in (lexical) static storage in the context. funArg (); // 10 instead of 20}) (foo) is saved when the function is created );

Technically, the data in the parent context of the function is stored in the internal attribute [Scope] of the function. If you do not know what [Scope] is, we recommend that you read the previous chapter, which describes [Scope] in detail. If you fully understand the [[Scope] and Scope chain knowledge, then you fully understand the closure.

According to the algorithm created by the function, we can see that in ECMAScript, all functions are closures, because they all save the scope chain of the Upper-layer context when they are created (except for exceptions) (whether or not the function will be activated later -- [[Scope] will be available when the function is created ):

Var x = 10; function foo () {alert (x);} // foo is the closure foo:
 
  
= {[Call]:
  , [[Scope]: [global: {x: 10}],... // other attributes };
 

As we said, for the purpose of optimization, when a function does not use free variables, the implementation may not be saved in the sub-scope chain. However, nothing in the ECMA-262-3 specification. Therefore, normally, all parameters are stored in the [[Scope] attribute during creation.

In some implementations, direct access to the closure scope is allowed. For example, for the [[Scope] attribute of a function, Rhino corresponds to a non-standard _ parent _ attribute:

Var global = this; var x = 10; var foo = (function () {var y = 20; return function () {alert (y );};})(); foo (); // 20 alert (foo. _ parent __. y); // 20foo. _ parent __. y = 30; foo (); // 30 // you can move to the top alert (foo. _ parent __. _ parent _ = global); // truealert (foo. _ parent __. _ parent __. x); // 10
All objects reference A [Scope]

Note that in ECMAScript, the closures created in the same parent context share a [Scope] attribute. That is to say, if a closure modifies the variable [[Scope], it will affect the reading of the variable by other closures:

Var firstClosure; var secondClosure; function foo () {var x = 1; firstClosure = function () {return ++ x ;}; secondClosure = function () {return -- x ;}; x = 2; // affects AO ["x"], alert (firstClosure () in two [Scope] public closed packets ()); // 3, through the [[Scope]} foo (); alert (firstClosure (); // 4 alert (secondClosure (); // 3

This means that all internal functions share the same parent scope.

There is a common misunderstanding about this function. Developers often fail to get the expected results when creating a function (counting internally) in a loop statement, it is expected that each function has its own value.

Var data = []; for (var k = 0; k <3; k ++) {data [k] = function () {alert (k );};} data [0] (); // 3, instead of 0 data [1] (); // 3, instead of 1 data [2] (); // 3, instead of 2

The above example proves that the closure created in the same context shares a [[Scope] attribute. Therefore, the variable "k" in the upper context can be easily changed.

ActiveContext. scope = [... // other variable objects {data: [...], k: 3} // activity object]; data [0]. [[Scope] === Scope; data [1]. [[Scope] === Scope; data [2]. [[Scope] = Scope;

In this way, when the function is activated, the final k used is changed to 3. As shown in the following figure, creating a closure can solve this problem:

Var data = []; for (var k = 0; k <3; k ++) {data [k] = (function _ helper (x) {return function () {alert (x) ;};}) (k); // input the "k" value} // The result is correct. data [0] (); // 0 data [1] (); // 1 data [2] (); // 2

Let's see what happened to the above Code? After the function "_ helper" is created, it is activated by passing in the "k" parameter. The returned value is also a function, which is saved in the corresponding array element. This technique produces the following results: When a function is activated, a new variable object will be created each time "_ helper" contains the parameter "x ", the value of "x" is the value of "k" passed in. In this way, the [[Scope] of the returned function is as follows:

Data [0]. [[Scope] = [... // activity object AO: {data: [...] in the parent context of other variable objects, k: 3}, _ activity object AO: {x: 0}] In the helper context; data [1]. [[Scope] = [... // activity object AO: {data: [...] in the parent context of other variable objects, k: 3}, _ activity object AO: {x: 1}] In the helper context; data [2]. [[Scope] = [... // activity object AO: {data: [...] in the parent context of other variable objects, k: 3}, _ activity object AO: {x: 2}] In the helper context;

We can see that the [[Scope] attribute of the function has the desired value. To achieve this purpose, we have to create additional variable objects in [[Scope. Note that, in the returned function, if you want to obtain the value of "k", the value will still be 3.

By the way, many articles about JavaScript believe that only the newly created function is the closure, which is wrong. In practice, this method is the most effective. However, theoretically, all functions in ECMAScript are closures.

However, the method mentioned above is not the only method. You can obtain the correct "k" value in other ways, as shown below:

Var data = []; for (var k = 0; k <3; k ++) {(data [k] = function () {alert (arguments. callee. x );}). x = k; // use k as an attribute of the function} // The result is also correct data [0] (); // 0 data [1] (); // 1 data [2] (); // 2
Funarg and return

Another feature is returned from the closure. In ECMAScript, the Return Statement in the closure returns the control flow to the call context (caller ). In other languages, for example, Ruby, there are many closure forms, and the corresponding processing closure returns are also different. The following methods are possible: they may be directly returned to the caller, or in some cases -- exit directly from the context.

The exit behavior of ECMAScript is as follows:

Function getElement () {[1, 2, 3]. forEach (function (element) {if (element % 2 = 0) {// return to the function "forEach" // instead of the getElement function alert ('found: '+ element); // found: 2 return element ;}}); return null ;}

However, in ECMAScript, try catch can achieve the following effects:

Var $ break ={}; function getElement () {try {[1, 2, 3]. forEach (function (element) {if (element % 2 = 0) {// from getElement "return" alert ('found: '+ element ); // found: 2 $ break. data = element; throw $ break;});} catch (e) {if (e = $ break) {return $ break. data ;}} return null;} alert (getElement (); // 2
Theoretical version

It is explained that developers often mistakenly interpret the closure as returning internal functions from the parent context, or even as that only anonymous functions can be closures.

Besides, because of the scope chain, all functions are closures (not related to function types: anonymous functions, FE, NFE, and FD are closures ).

Only one type of Function exists, that is, the Function created through the Function constructor, because its [[Scope] only contains global objects. To better clarify this issue, we provide two correct version definitions for the closure in ECMAScript:

In ECMAScript, the closure refers:

  1. Theoretically: All functions. Because they all save the upper-layer context data when they are created. This is true even for simple global variables, because accessing global variables in a function is equivalent to accessing free variables. In this case, the outermost scope is used.
  2. From a practical perspective: The following functions are regarded as closures:
    • Even if the context for creating it has been destroyed, it still exists (for example, the internal function returns from the parent function)
    • A free variable is referenced in the code.
Additional reading

The topic list of this article is as follows:

  1. How should we understand the working principle of the JavaScript engine?
  2. JavaScript exploration: the importance of writing maintainable code
  3. JavaScript exploration: exercise caution when using global variables
  4. JavaScript exploration: var pre-parsing and side effects
  5. JavaScript exploration: for Loop (for Loops)
  6. JavaScript exploration: for-in loop (for-in Loops)
  7. Exploring JavaScript: Prototypes is too powerful
  8. JavaScript: eval () is the devil"
  9. JavaScript exploration: Using parseInt () for Numerical Conversion
  10. Exploring JavaScript: Basic coding specifications
  11. JavaScript exploration: function declaration and function expression
  12. JavaScript exploration: Name function expressions
  13. JavaScript: function name in the debugger
  14. JavaScript: JScript Bug
  15. JavaScript exploration: Memory Management of JScript
  16. Exploring JavaScript: SpiderMonkey's quirks
  17. JavaScript exploration: an alternative solution to naming function expressions
  18. JavaScript exploration: Object
  19. JavaScript exploration: Prototype chain
  20. JavaScript exploration: Constructor
  21. JavaScript probing: executable context Stack
  22. Execution context 1: Variable object and activity object
  23. Execution context 2: Scope chain Scope Chains
  24. Execution context 3: Closure Closures
  25. Execution context 4: This pointer
  26. Exploring JavaScript: Powerful prototype and prototype chain
  27. JavaScript Functions 1: function declaration
  28. JavaScript function 2: function expressions
  29. JavaScript function 3: function expressions in a group
  30. JavaScript function 4: function Constructor
  31. JavaScript variable object 1: VO Declaration
  32. JavaScript variable object 2: VO in different execution contexts
  33. JavaScript variable object 3: two stages of execution Context
  34. JavaScript variable object IV: Variables
  35. Property of the JavaScript variable object __parent _
  36. JavaScript scope chain 1: Scope chain Definition
  37. JavaScript scope chain 2: function Lifecycle
  38. JavaScript scope chain 3: Scope chain features
  39. JavaScript closure 1: Introduction to closures
  40. JavaScript closure 2: Implementation of closure
  41. JavaScript closure 3: Closure usage

This article is available at http://www.nowamagic.net/librarys/veda/detail/1708.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.