原文章:
《JavaScript世界的一等公民 - 函数》
其中2.3节非常清晰明了的解释了闭包。以前从其他文章看到的解释都不是很明了,看了2.3节终于明白了。
摘录如下:
2.2 (摘一部分,因为2.3会涉及到)
function generator() {
var i = 0;
return function() {
return i++;
};
}
var gen1 = generator(); // 得到一个自然数生成器
var gen2 = generator(); // 得到另一个自然数生成器
var r1 = gen1(); // r1 = 0
var r2 = gen1(); // r2 = 1
var r3 = gen2(); // r3 = 0
var r4 = gen2(); // r4 = 1
上面的代码展示了把函数作为返回值的示例,generator是一个自然数生成器函数,
返回值是一个自然数生成函数。
每次调用generator时都会把一个匿名函数作为结果返回,这个匿名函数在被实际调用时依次返回每个自然数。
在generator里的变量i在每次调用这个匿名函数时都会自增1,这其实就是一个闭包。
下面我们来介绍一下闭包.
2.3 闭包(Closure)
var elem = document.getElementById('test');
elem.addEventListener('click', function() {
alert('You clicked ' + elem.tagName);
});
这段代码的作用是点击一个结点时显示它的标签名称,它把一个匿名函数注册为一个DOM结点的click事件处理函数,函数内引用了一个DOM对象elem,就形成了闭包。这就会产生一个循环引用,即:DOM->闭包->DOM->闭包...DOM对象在闭包释放之前不会被释放;而闭包作为DOM对象的事件处理函数存在,所以在DOM对象释放前闭包不会释放,即使DOM对象在DOM tree中删除,由于这个循环引用的存在,DOM对象和闭包都不会被释放。可以用下面的方法可以避免这种内存泄露:
var elem = document.getElementById('test');
elem.addEventListener('click', function() {
alert('You clicked ' + this.tagName); // 不再直接引用elem变量
});
上面这段代码中用this代替elem(在DOM事件处理函数中this指针指向DOM元素本身),让JS运行时不再认为这个函数中使用了父类的变量,因此不再形成闭包。
闭包还会带来很多类似的内存泄露问题,只有在写代码的时候着重注意一下闭包,尽量避免此类的问题产生。