对闭包最好的解释
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures
废话不多说,直接看例子:
1 function makeFunc() { 2 var name = "Mozilla"; 3 function displayName() { 4 alert(name); 5 } 6 return displayName; 7 } 8 9 var myFunc = makeFunc(); 10 myFunc();
这里的myFunc就是闭包,闭包本身也是个函数,它是嵌套在父函数里面被父函数返回的那个函数,它共享了父函数的环境,能获取父函数里定义的变量。在这里,父函数其实就变成了一个函数工厂,创建出来的函数赋给谁,谁就是闭包。
闭包有啥特点?按理makeFunc函数在执行完后(倒数第二条语句),里面的name变量应该就消失了,但是下一条myFunc()语句还是访问到name变量,这就是闭包的魔力。
这是因为闭包是由两部分组成的,函数,以及创建该函数的环境。
闭包可以用在什么地方?试着理解这句话:可以使用只有一个方法的对象的地方,都可以使用闭包。还是直接看例子:
1 function makeSizer(size) { 2 return function() { 3 document.body.style.fontSize = size + 'px'; 4 }; 5 } 6 7 var size12 = makeSizer(12); 8 var size14 = makeSizer(14); 9 var size16 = makeSizer(16);
这是一个放大字号功能的JS代码,把size12\size14\size16分别绑定到不同的按钮上,就实现了点击相应的按钮,变成相应的字号。在这里,按钮是对象,按钮只有一个改变字号的方法,所以可以使用闭包。
再深化下,看一个更高级的例子:
1 var Counter = (function() { 2 var privateCounter = 0; 3 function changeBy(val) { 4 privateCounter += val; 5 } 6 return { 7 increment: function() { 8 changeBy(1); 9 }, 10 decrement: function() { 11 changeBy(-1); 12 }, 13 value: function() { 14 return privateCounter; 15 } 16 } 17 })(); 18 19 console.log(Counter.value()); /* logs 0 */ 20 Counter.increment(); 21 Counter.increment(); 22 console.log(Counter.value()); /* logs 2 */ 23 Counter.decrement(); 24 console.log(Counter.value()); /* logs 1 */
这里有两点高级货要讲,一是使用了匿名函数自执行,即var Counter = (func(){})();这种情况下,其实就相当于把本文的第一个例子的前两条语句合并成了一条语句,此时Counter成了闭包。二是外围函数里返回的不是一个函数,而是好几个函数,这些方法共享同样的环境,但函数不一样,记住闭包的定义,闭包=函数+环境。
有一个技巧对于理解闭包很有作用,一定要讲下,举个例子:
1 function makeAdder(x) { 2 return function(y) { 3 return x + y; 4 }; 5 } 6 7 var add5 = makeAdder(5); 8 var add10 = makeAdder(10); 9 10 console.log(add5(2)); // 7 11 console.log(add10(2)); // 12
第7,8行的代码乍看之下非常费解,其实要理解很简单,只要把=号右边改写下,写成
var add5 = function(y){ return 5 + y; }
写成这种形式后,对于为什么add5(2)是7就一目了然了?那么怎么就确定要这么写呢?很简单,你把return后面的东西照抄就行,里面的已知变量记得换掉,就是这么简单。这种简单的可能无须这么做,但是遇到复杂的闭包的时候,这么做就非常有助于理解了。