• javascript closure


    http://javascript.info/tutorial/closures

     

    Closures


    1. Access to outer variables
    2. Nested functions
    3. Closures
      1. Mutability of LexicalEnvironment
      2. The notorious closure loop
      3. [[Scope]] for new Function
    4. Summary

    From the previous article, we know that a variable is a property of the LexicalEnvironment object.

    Here we discuss access to outer variables and nested functions. In-depth understanding of closures follows automatically.

    Access to outer variables

    What if a variable is accessed, but it isn’t local? Like here:

    1 var a = 5
    2  
    3 function f() {
    4   alert(a)
    5 }

    In this case, the the interpreter finds the variable in the outer LexicalEnvironment object.

    The process consists of two steps:

    1. First, when a function f is created, it is not created in an empty space.

      There is a current LexicalEnvironment object. In the case above, it’s window (a is undefined at the time of function creation).

      When a function is created, it gets a hidden property, named [[Scope]], which references currentLexicalEnvironment.

    2. Later, when the function runs, it creates it’s own LexicalEnvironment and links it with [[Scope]].

      So when a variable is not found in the local LexicalEnvironment, it is searched outside:

    If a variable is read, but can not be found anywhere, the error is generated.

    function f() {
      alert(x) // reading x gives error, no x
    }

    Certain language constructs block the error, for example typeof x works if there is no x (and returnsundefined), but that’s an exception.

    If a variable is set, but not found anywhere, then it is created in the outmost LexicalEnvironment, which iswindow.

    function f() {
      x = 5 // writing x puts it into window
    }

    Nested functions

    Functions can be nested one inside another, forming a chain of LexicalEnvironments which can also be called a scope chain.

    01 var a = 1
    02 function f() {
    03  
    04   function g() {
    05     alert(a)
    06   }
    07  
    08   return 
    09 }
    10  
    11 var func = f()
    12 func() // 1

    LexicalEnvironments form a chain (from inside out):

    01 // LexicalEnvironment = window = {a:1, f: function}
    02 var a = 1
    03 function f() {
    04   // LexicalEnvironment = {g:function}
    05  
    06   function g() {
    07     // LexicalEnvironment = {}
    08     alert(a)
    09   }
    10  
    11   return 
    12 }

    So, function g has access to g, a and f.

    Create a function sum that will work like that: sum(a)(b) = a+b.

    Yes, the syntax is dual brackets. Funny, isn’t it? For instance:

    sum(1)(2) = 3
    sum(5)(-1) = 4
    Open solution

    Create a function sum that will work like that: sum(a)(b) = a+b and accepts any number of brackets.

    For instance:

    1 sum(1)(2) == 3
    2 sum(5)(-1)(2) == 6
    3 sum(6)(-1)(-2)(-3) == 0
    4 sum(0)(1)(2)(3)(4)(5) == 15
    Open hint 1Open solution

    Closures

    Nested function may continue to live after the outer function has finished:

    1 function User(name) {
    2      
    3   this.say = function(phrase) { 
    4     alert(name + ' says: ' + phrase)
    5   }
    6  
    7 }
    8  
    9 var user = new User('John')

    Marking up LexicalEnvironments:

    Note, the this context is not related to scopes and variables. It does not participate here.

    As we see, this.say is a property in the user object, so it continues to live after User completed.

    And if you remember, when this.say is created, it (as every function) gets an internal referencethis.say.[[Scope]] to current LexicalEnvironment. So, the LexicalEnvironment of the current Userexecution stays in memory. All variables of User also are it’s properties, so they are also carefully kept, not junked as usually.

    The whole point is to ensure that if the inner function wants to access an outer variable in the future, it is able to do so.

    • The inner function keeps a reference to the outer LexicalEnvironment.
    • The inner function may access variables from it any time even if the outer function is finished.
    • The browser keeps the LexicalEnvironment and all it’s properties(variables) in memory until there is an inner function which references it.

    This is called a closure.

    Mutability of LexicalEnvironment

    Several function may share same outer LexicalEnvironment. In this case they can modify it’s properties.

    In the example below, this.fixName changes name, which is used by this.say:

    01 function User(name) {
    02      
    03   this.fixName = function() {
    04     name = 'Mr.' + name.toUpperCase()
    05   }
    06  
    07   this.say = function(phrase) { 
    08     alert(name + ' says: ' + phrase)
    09   }
    10  
    11 }
    12  
    13 var user = new User('John')
    14 // (1)
    15 user.fixName()
    16 // (2)
    17 user.say("I'm alive!"// Mr.JOHN says: I'm alive!

    Here user.fixName.[[Scope]] and user.say.[[Scope]] reference same LexicalEnvironment, which corresponds to new User run.

    From (1) to (2), the LexicalEnvironment.name is updated, so both functions see the variable change.

    Variables in outer LexicalEnvironment may change.

    Inner functions always see the last value.

    The notorious closure loop

    The task below contains interesting tricks, best demonstrated by an example. Please, glance at the solution, or, much better, try to solve it.

    Here is a function to create an army of shooters:

    01 function makeArmy() {
    02  
    03   var shooters = []
    04  
    05   for(var i=0; i<10; i++) {
    06     var shooter = function() { // a shooter is a function
    07       alert(i) // which should alert it's number
    08     }
    09     shooters.push(shooter)   
    10   }
    11  
    12   return shooters
    13 }
    14  
    15 var army = makeArmy()
    16  
    17 var shooter = army[0] // first shooter
    18 shooter() // alerts 10, should be 0
    19  
    20 shooter = army[5] // 5th shooter
    21 shooter() // alerts 10, should be 5
    22  
    23 // all shooters alert same: 10 instead of 1,2,3...10.

    Why all shooters alert the same? How to make each shooter output it’s number?

    Solution

    Note that the shooter function. Does not have a variable named i.

    So, when it is called, the interpreter takes i from the outer LexicalEnvironment.

    The problem is that at the time when shooters are run, function makeArmy has already finished the execution.

    The loop has finished and the variable i is now 10.

    There are two possible solutions.

    The first one is to put the correct value into the shooting function itself.

    01 function makeArmy() {
    02  
    03   var shooters = []
    04  
    05   for(var i=0; i<10; i++) {
    06  
    07     var shooter = function() {
    08       alert( arguments.callee.i )
    09     }
    10     shooter.i = i
    11  
    12     shooters.push(shooter)   
    13   }
    14  
    15   return shooters
    16 }
    17  
    18 var army = makeArmy()
    19  
    20 army[0]() // 0
    21 army[1]() // 1

    Another, more advanced solution is to use an extra function to trap the current value of i:

    01 function makeArmy() {
    02  
    03   var shooters = []
    04  
    05   for(var i=0; i<10; i++) {
    06  
    07     var shooter = function(i) {
    08  
    09       return function() {
    10         alert( i )
    11       }
    12  
    13     }(i)
    14  
    15     shooters.push(shooter)   
    16   }
    17  
    18   return shooters
    19 }
    20  
    21 var army = makeArmy()
    22  
    23 army[0]() // 0
    24 army[1]() // 1

    Let’s consider the highlighted fragment more thoroughly.

    1 var shooter = function(i) {
    2   return function() {
    3     alert( i )
    4   }
    5 }(i)

    Here, the actual shooting function is created as the result of an anonymous function(i)which is created and executed in one place.

    So, when it comes to executing alert(i), it will be taken from LexicalEnvironment of the anonymous function.

    So, the anonymous function traps current i into it’s LexicalEnvironment and allows the shooter to access it.

    The last way is to wrap the whole loop into temporary function. Sometimes that’s more readable:

    01 function makeArmy() {
    02  
    03   var shooters = []
    04  
    05   for(var i=0; i<10; i++) (function(i) {
    06  
    07     var shooter = function() {
    08       alert( i )
    09     }
    10      
    11     shooters.push(shooter)
    12     
    13   })(i)
    14  
    15   return shooters
    16 }
    17  
    18 var army = makeArmy()
    19  
    20 army[0]() // 0
    21 army[1]() // 1

    The (function(i) { ... }) definition is wrapped into brackets to make sure the interpreter treats that as expression.

    [[Scope]] for new Function

    There is an exception to general scope binding rule. When you create a function using new Function, it’s[[Scope]] points to window, not to current LexicalEnvironment.

    The following example demonstrates how a function, created with new Function ignores local variable aand outputs the global variable.

    The regular behavior:

    01 window.a = 1;
    02 function getFunc() {
    03   var a = 2;
    04   
    05   var func = function() { alert(a) }
    06  
    07   return func;
    08 }
    09  
    10 getFunc()() // 2, from LexicalEnvironemnt of getFunc

    And now the function, created by new Function:

    1 window.a = 1
    2 function getFunc() {
    3   var a = 2
    4   
    5   var func = new Function('''alert(a)'
    6   return func
    7 }
    8  
    9 getFunc()() // 1, from window

     http://javascript.info/tutorial/closures

    Summary

    We discussed the following topics:

    • How variables are handled in JavaScript.
    • How scopes work.
    • What is a closure and how to use it.
    • Possible pitfalls and subtles in working with closures.

    Closures in JavaScript is like a salt. You can live without it, but not very long. Usually people put it everywhere…

    See also:
  • 相关阅读:
    【总结】Centos中,Kerberos安装
    Go语言mgo
    Go语言mgo
    理解 Serenity,Part-1:深度抽象
    ZCash零知识证明
    零知识证明(Zero-Knowledge Proof)原理详解:非交互式证明实现自动验证防止作假
    tf.shape(x)、x.shape、x.get_shape()函数解析(最清晰的解释)
    Java生成(m.n)之间的随机数
    tf.cond()函数解析(最清晰的解释)
    OpenStack Blazar 架构解析与功能实践
  • 原文地址:https://www.cnblogs.com/oxspirt/p/5056834.html
Copyright © 2020-2023  润新知