一、什么是闭包?
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
例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错误