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)

  • 相关阅读:
    MySQL的sql_mode合理设置
    Redis
    启动Jupyter Notebook提示ImportError: DLL load failed: 找不到指定的模块。
    Linux目录结构
    修改mysql密码报错: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '
    springmvc运行原理
    博客园美化
    数据搜索
    git
    window
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/6504781.html
Copyright © 2011-2022 走看看