javascript理解之闭包

一、闭包的概念

闭包 是指有权访问另一个函数作用域中的变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量。

二、闭包的作用

闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

三、使用闭包的好处

  1. 希望一个变量长期驻扎在内存中
  2. 避免全局变量的污染
  3. 私有成员的存在

四、闭包的缺点:

常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。

五、闭包的原理:

先看一段代码:

1
2
3
4
5
6
7
8
9
10
11
var x = 20;

function foo() {
alert(x); // 自由变量"x" == 20
}

// 为foo闭包
fooClosure = {
call: foo // 引用到function
lexicalEnvironment: {x: 20} // 搜索上下文的上下文
};

上述例子中,“fooClosure”部分是伪代码。对应的,在ECMAScript中,“foo”函数已经有了一个内部属性——创建该函数上下文的作用域链。“lexical”通常是省略的。上述例子中是为了强调在闭包创建的同时,上下文的数据就会保存起来。当下次调用该函数的时候,自由变量就可以在保存的(闭包)上下文中找到了。

对于要实现将局部变量在上下文销毁后仍然保存下来,基于栈的实现显然是不适用的(因为与基于栈的结构相矛盾)。因此在这种情况下,上层作用域的闭包数据是通过 动态分配内存的方式来实现的(基于“堆”的实现)。

六、使用闭包

  1. 全局变量的累加

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <script>
    function outer(){
    var x=10;
    return function(){ //函数嵌套函数
    x++;
    alert(x);
    }
    }
    var y = outer(); //外部函数赋给变量y;
    y(); //y函数调用一次,结果为11,相当于outer()();
    y(); //y函数调用第二次,结果为12,实现了累加
    </script>
  2. 模块化代码,减少全局变量的污染

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <script>
    var abc = (function(){ //abc为外部匿名函数的返回值
    var a = 1;
    return function(){
    a++;
    alert(a);
    }
    })();
    abc(); //2 ;调用一次abc函数,其实是调用里面内部函数的返回值
    abc(); //3
    </script>
  3. 私有成员的存在

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <script>
    var aaa = (function(){
    var a = 1;
    function bbb(){
    a++;
    alert(a);
    }
    function ccc(){
    a++;
    alert(a);
    }
    return {
    b:bbb, //json结构
    c:ccc
    }
    })();
    aaa.b(); //2
    aaa.c() //3
    </script>
  4. 在循环中直接找到对应元素的索引

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <script>
    window.onload = function(){
    var aLi = document.getElementsByTagName('li');
    for (var i=0;i<aLi.length;i++){
    aLi[i].onclick = function(){ //当点击时for循环已经结束
    alert(i);
    };
    }
    }
    </script>

    <body>
    <ul>
    <li>123</li>
    <li>456</li>
    <li>789</li>
    <li>010</li>
    </ul>
    </body>
  1. 使用闭包改写上面代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <script>
    window. onload = function(){
    var aLi = document.getElementsByTagName('li');
    for (var i= 0;i < aLi.length;i++){
    (function(i){
    aLi[i].onclick = function(){
    alert(i);
    };
    })(i);
    }
    };
    </script>
    <body>
    <ul>
    <li>123</li>
    <li>456</li>
    <li>789</li>
    </ul>
    </body>

七、特别提醒

(function(){})(i) 中也存在闭包的原因: function(){}是一个函数,(function(){})(i)中也是个函数,函数中嵌套函数,且变量i相对于function(){}是一个函数是上下文变量,会记住i的值。

转自:http://segmentfault.com/a/1190000000652891