zoukankan      html  css  js  c++  java
  • Javascript 闭包

    闭包

        闭包是含有自由变量的函数。自由变量指的是不是函数局部变量,且不是函数参数的变量。比如

    var a = 10;
    function test(m){
         alert(a + m);
    }
    test(10);
    

        上面代码中,a就是函数test的自由变量,test也就是一个闭包。至于test能够访问到a,是因为在函数test被定义的时候,它的[[scope]]属性中就包含了全局变量a;之后在test被执行的时候,它的Execution context的Scope Chain中就可以找到a.

    闭包原理

        在javascript中,使用静态作用域。即函数的作用域在函数定义的时候被确定,函数运行时它内部用到的变量,就从该静态作用域中查找。

    var x = 10;
    function foo() {
      alert(x);
    }
    /*
    此时,foo的 [[scope]]  = 
    {
    x: 10,
    this:window
    ...
    }
    */
    (function (funArg) {
      var x = 20;
      // 变量"x"在(lexical)上下文中静态保存的,在该函数创建的时候就保存了
      funArg(); // 10, 而不是20
    })(foo);
    /*
    匿名函数的[[scope]] 为 {x:20, this:window, ....}
    
    匿名函数执行的时候,其Execution context的scope chain为 
    AO(匿名函数) + 匿名函数的[[scope]] = AO(匿名函数) -- > VO(global),即为
    {x:20, funArg: reference of function} --> {x:10, this:window, ....}
    
    当foo在匿名函数内部被执行的时候,它的Execution context的scope chain为 
    AO(foo) + foo.[[scope]]
    AO(foo)  -- > VO(global),即 {} -->{x:10, this:window}
    
    */
    

        在一个函数被创建的时候,函数的内部属性[[scope]]中就保存了其父级上下文的所有参数信息。在上面的例子中,函数foo 在创建时,保存了其父级上下文的参数 x =10, 在foo作为参数传递给 匿名函数,并在匿名函数中被调用时,尽管匿名函数内部有变量x,但是函数 foo被调用时,它的作用域链是使用AO(foo) + foo.[[scope]]构成,索引x的时候,返回10(见上面注释).

        如果上面的程序中,没有在在全局位置处声明x,即:

    function foo() {
      alert(x);
    }
    
    (function (funArg) {
      var x = 20;
      // 变量"x"在(lexical)上下文中静态保存的,在该函数创建的时候就保存了
      funArg(); // 10, 而不是20
    })(foo);
    /*
    匿名函数的[[scope]] 为 {x:20, this:window, ....}
    
    匿名函数执行的时候,其Execution context的scope chain为 
    AO(匿名函数) + 匿名函数的[[scope]] = AO(匿名函数) -- > VO(global),即为
    {x:20, funArg: reference of function} --> {this:window, ....}
    
    当foo在匿名函数内部被执行的时候,它的Execution context的scope chain为 
    AO(foo) + foo.[[scope]]
    AO(foo)  -- > VO(global),即 {} -->{this:window}
    */
    

    则尽管在匿名函数中声明了变量x,但是执行foo的时候其作用域链不包含匿名函数的AO, 会报错x is undefined.

        因此,闭包就是包含了父上下文中自由变量的函数,它能够引用定义在它外部的自由变量,是因为js的scope chain和VO/AO和[[scope]]的机制(见Javascript 运行上下文和作用域链);且它的[[scope]]在它被定义的时候就确定了,而不是在运行的时候确定。

    所有对象都引用同一个[[scope]]

        在ECMAScript中,同一个父上下文中创建的闭包是共用同一个[[scope]]属性,即某个闭包对其中[[scope]]的变量做修改会影响到其他闭包对其变量的读取。
    如:

    var firstClosure;
    var secondClosure;
    function foo() {
      var x = 1;
      firstClosure = function () { return ++x; };
      secondClosure = function () { return --x; };
      x = 2; // 影响 AO["x"], 在2个闭包公有的[[Scope]]中
      alert(firstClosure()); // 3, 通过第一个闭包的[[Scope]]
    }
    foo();
    alert(firstClosure()); // 4
    alert(secondClosure()); // 3
    
    以及下面的:
    var data = [];
    for (var k = 0; k < 3; k++) {
      data[k] = function () {
        alert(k);
      };
    }
    data[0](); // 3, 而不是0
    data[1](); // 3, 而不是1
    data[2](); // 3, 而不是2
    data[k] 的 [[scope]] 都是 {k: xx, ...}, 这是共用的,当k变更的时候会影响到data[]的[[scope]].
    
     

        那么,如何修改上面的代码使得data[k] = k呢?一种方法是封装一层函数,增加一层作用域,在该层作用域中保存k的值。

    var data = [];
    
    for (var k = 0; k < 3; k++) {
      data[k] = (function _helper(x) {
        return function () {
          alert(x);
        };
      })(k); // 传入"k"值
    }
    
    // 现在结果是正确的了
    data[0](); // 0
    data[1](); // 1
    data[2](); // 2
    
    data[0].[[Scope]] === [
      ... // 其它变量对象
      父级上下文中的活动对象AO: {data: [...], k: 3},
      _helper上下文中的活动对象AO: {x: 0}
    ];
    
    data[1].[[Scope]] === [
      ... // 其它变量对象
      父级上下文中的活动对象AO: {data: [...], k: 3},
      _helper上下文中的活动对象AO: {x: 1}
    ];
    
    data[2].[[Scope]] === [
      ... // 其它变量对象
      父级上下文中的活动对象AO: {data: [...], k: 3},
      _helper上下文中的活动对象AO: {x: 2}
    ];
    
     

    参考
    深入理解JavaScript系列(16):闭包(Closures)

  • 相关阅读:
    PAT (Advanced Level) 1080. Graduate Admission (30)
    PAT (Advanced Level) 1079. Total Sales of Supply Chain (25)
    PAT (Advanced Level) 1078. Hashing (25)
    PAT (Advanced Level) 1077. Kuchiguse (20)
    PAT (Advanced Level) 1076. Forwards on Weibo (30)
    PAT (Advanced Level) 1075. PAT Judge (25)
    PAT (Advanced Level) 1074. Reversing Linked List (25)
    PAT (Advanced Level) 1073. Scientific Notation (20)
    PAT (Advanced Level) 1072. Gas Station (30)
    PAT (Advanced Level) 1071. Speech Patterns (25)
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/6504781.html
Copyright © 2011-2022 走看看