JavaScript 中的闭包

一个闭包,简单地说,就是一个函数以及其定义时的上下文环境。

用代码来解释会更直观一点:

1
2
3
4
5
6
7
8
9
function outerFn() {
    var name = "outer";
    function innerFn() {
        alert(name);
    }
    return innerFn;
}
var myFunc = outerFn();
myFunc();   // output "outer"

这样,myFunc 便是一个闭包,其包括了函数 innerFn() 和其上下文环境 name

多个闭包共享上下文环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var myObject = function(inc) {
    var value = 0;
    return {
        increment: function(inc) {
            value += typeof inc === 'number' ? inc : 1;
        },
        decrement: function(inc) {
            value -= typeof inc === 'number' ? inc : 1;
        },
        getValue: function() {
            return value;
        }
    }
}();
alert(myObject.getValue()); //output 0
myObject.increment(2);     
alert(myObject.getValue()); //output 2
myObject.decrement(4);
alert(myObject.getValue()); //output -2

循环中的闭包

考虑以下代码:

1
2
3
<p>aaaa</p>
<p>bbbb</p>
<p>cccc</p>

1
2
3
4
5
6
7
8
9
10
var add_the_handlers = function (nodes) {
    var i;
    for (i = 0; i < nodes.length; i += 1) {
        nodes[i].onclick = function (e) {
            alert(i);  
        }
    }
    // i = 100;  // for test
};
add_the_handlers(document.getElementsByTagName('p'));

期望在点击段落时得到每个节点的序号,可总是得到总结点数3。

原因是在触发事件前首先需要为每个节点绑定事件,完成后局部变量 i 已为3。for 循环创建了3个闭包,共享了同一个环境,读取到的 i 都是3。一个测试是将 i = 100那句启用,可以发现,点击得到的都是100。

解决方法

将参数 i 传递进去,为每一个事件创立一个上下文不同的闭包。

1
2
3
4
5
6
7
8
9
10
11
var add_the_handlers = function (nodes) {
    var i;
    for (i = 0; i < nodes.length; i += 1) {
        nodes[i].onclick = function (i) {  // mark 1
            return function(e){
                alert(i);   // mark 2, mark 1中的function(i)和此处的i可替换为j等
            };
        }(i);   //mark 3, 传递参数给onclick函数,*/
    }
};
add_the_handlers(document.getElementsByTagName('p'));

《JavaScript 中的闭包》有20个想法

  1. 直白点讲,闭包就是为了避免对象被JS的垃圾回收机制给消除。。JS里全是对象,函数在JS里也是对象,通过子函数对变量的引用,避免父函数执行完毕被回收,其在内存中的栈区依然存在。。

    1. @QiQiBoY, 嗯,今天算是比较认真地去看了一下闭包。不过,在for循环那里卡了好是一阵子

    1. @Ray Chow, 我也还是半吊子,闭包还是有点困难呢!

评论已关闭。