1 闭包的含义
1.1MDN上对闭包的解释
函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起构成闭包(closure)。也就是说,闭包可以让你从内部函数访问外部函数作用域。在 JavaScript 中,每当函数被创建,就会在函数生成时生成闭包。
1.2我对闭包的理解
每个函数都是一个闭包,函数F()在其私有空间定义的变量在函数空间外是不可见的,由于F()是可以在全局空间中被调用的,所以在函数中新定义并返回一个新的Inner()函数,用来返回F()的的私有变量,从而生成一个可以访问F()私有空间的新的全局函数。
下面是一个简单的闭包的例子:
function F(param) {
var N = function() {
return param;
}
param++;
return N;
}
>var inner = F(123)
>inner();
124
inner()返回的是递增更新之后的值,由此可以看出,函数所绑定的是作用域本身,而不是一个值。
2 常见的闭包错误
2.1 错误代码
function F() {
var arr = [], i;
for (i = 0; i < 3; i++) {
arr[i] = function () {
return i;
};
}
return arr;
}
> var arr = F();
>arr[0] () ; >arr[1] () ; >arr[2] () ;
3 3 3
这并不是我们想要的结果,因为我们在这里创建了三个闭包,它们都指向了一个共同的局部变量i,但是闭包并不会记录它们的值,它们所拥有的只是相关域在创建时的一个连接(即引用)。
2.2 正确代码:
方法一:即时函数
function F() {
var arr = [], i;
for( i = 0; i < 3; i++) {
arr[i] = (function (x) {
return function () {
return x;
}
}(i));
}
return arr;
}
在这里,不再创建一个返回i的函数了,而是将i 传递给了另一个即时函数,在该函数中,i被赋值给了局部变量x,这样一来,每次迭代中的x就会拥有各种不同的值了。
方法二:普通内部函数
function F() {
function binder(x) {
return function() {
return x;
};
}
var arr = [], i ;
for (i = 0; i < 3; i++) {
arr[i] = binder(i);
}
return arr;
}
该方法是在每次迭代过程中,在中间函数内将i的值本地化。
3 闭包的应用
3.1 getter与setter
假设现在有一个变量,表示某个特定区间内的值,我们不想把它暴露给外部,所以将它保护在相关函数的内部,然后引入两个额外的函数——一个用于获取变量值,另一个用于给变量重新赋值。
3.2 迭代器
function setup(x) {
var i = 0;
return function() {
return x[i++];
};
}
> var next = setup(['a','b','c']);
>next();
3.3回调
function changeSize(size){
return function(){ document.body.style.fontSize = size + 'px'; };
}
var size12 = changeSize(12);
var size14 = changeSize(20);
document.getElementById('size-12').onclick = size12;
document.getElementById('size-20').onclick = size14;
作为一个回调(事件触发时调用的函数)绑定到事件。
参考:JavaScript面向对象编程指南(第二版)