一、什么是闭包?

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
例1:

1
2
3
4
5
6
7
8
9
10
function makeFunc() {
var name = "Mozilla";
function displayName() {
alert(name);
}
return displayName;
}
var myFunc = makeFunc();
myFunc();

上面函数makeFunc,返回了一个函数displayName,而该函数在其内部引用了局部变量name。当makeFunc返回diasplayName函数时,变量name保存在返回的函数(displayName)中。
因此,makeFunc就是一个闭包,它由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成。在这个例子中,myFunc由 displayName 函数和闭包创建时存在的 “Mozilla” 字符串形成。

二、闭包里的函数不会立刻执行

例2:

1
2
3
4
5
6
7
8
9
10
11
12
13
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

以上例子的显示结果为点击12时大小为12px,点击14时大小为14px,点击16时大小为16px。
刚开始我会质疑为什么要将函数makeSizer写成闭包,写成下面这样为什么不行?

1
2
3
4
5
6
function makeSizer(size) {
document.body.style.fontSize = size + 'px';
}
document.getElementById('size-12').onclick = makeSizer(12);
document.getElementById('size-14').onclick = makeSizer(14);
document.getElementById('size-16').onclick = makeSizer(16);

原因是:出现makeSizer(参数)时,这个函数就已经被调用,调用之后就回之行里面的内容,如果不想立刻执行函数里面的内容,即等到onclick事件发生时再执行,那么就要用到闭包,即函数里返回一个函数。

三、在循环中创建变量

例3:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function count() {
var arr = [];
for (var i=1; i<=3; i++) {
arr.push(function () {
return i * i;
});
}
return arr;
}
var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];
f1(); // 16
f2(); // 16
f3(); // 16

例3的结果为16、16、16
为什么不是1、4、9呢?
因为执行var results=count();这句语句时,count()函数已经执行,执行内容为网arr数组里塞了三个函数,此时这三个函数虽然被返回但并没有立刻执行,而且此时变量i已经变成了4,当执行下面的var f1=…三条语句时,这三个函数才执行所以结果全都是16!

返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function count() {
var arr = [];
for (var i=1; i<=3; i++) {
arr.push((function (n) {
return function () {
return n * n;
}
})(i));
}
return arr;
}
var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];
f1(); // 1
f2(); // 4
f3(); // 9

“创建一个匿名函数并立刻执行”的语法:

1
2
3
(function (x) {
return x * x;
})(3); // 9

需要用括号将整个函数定义包括起来,不然由于JavaScript语法解析的问题,会报SyntaxError错误