个人理解
var data = [];for (var i = 0; i < 3; i++) {data[i] = (function (i) {return function(){console.log(i);}})(i);}data[0]();//0data[1]();//1data[2]();//2
上面的循环相当与做了下面的事
var data = [];data[0] = (function (i) {return function(){console.log(i);}})(0);//相当于data[0] = function(){console.log(0);}data[1] = (function (i) {return function(){console.log(i);}})(1);//相当于data[1] = function(){console.log(1);}data[2] = (function (i) {return function(){console.log(i);}})(2);//相当于data[2] = function(){console.log(2);}data[0]();//0data[1]();//1data[2]();//2
声明了三个自执行函数,这三个函数之间是没有联系的,每个都是独立的(相当于3个实例,参考下面的makeAdder函数和makeCounter函数)
data[0] 、 data[1] 、data[2] 都是闭包。它们共享相同的函数定义,但是保存了不同的词法环境。在 data[0] 的环境中,i 为 0。在 data[1] 的环境中,i 为 1。在 data[2] 的环境中,i 为 2。
网上案例
闭包是通过作用域链来将函数内部变量保存在内存中
function f1(){var n=999;nAdd=function(){n+=1}function f2(){alert(n);}return f2;}var result=f1();result(); // 999nAdd();result(); // 1000
这段代码中另一个值得注意的地方,就是”nAdd=function(){n+=1}”这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。此处摘自阮一峰
function makeAdder(x) {return function(y) {return x + y;};}var add5 = makeAdder(5);var add10 = makeAdder(10);console.log(add5(2)); // 7console.log(add10(2)); // 12
add5 和 add10 都是闭包。它们共享相同的函数定义,但是保存了不同的词法环境。在 add5 的环境中,x 为 5。而在 add10 中,x 则为 10。
var makeCounter = function(){var privateCounter = 0function changeBy(val){privateCounter += val}return {increment:function(){changeBy(1)},decrement:function(){changeBy(-1)},increment:function(){return privateCounter}}}var Conter1 = makeCounter();var Conter2 = makeCounter();console.log(Counter1.value()); /* logs 0 */Counter1.increment();Counter1.increment();console.log(Counter1.value()); /* logs 2 */Counter1.decrement();console.log(Counter1.value()); /* logs 1 */console.log(Counter2.value()); /* logs 0 */
请注意两个计数器 Counter1 和 Counter2 是如何维护它们各自的独立性的。每个闭包都是引用自己词法作用域内的变量 privateCounter 。
每次调用其中一个计数器时,通过改变这个变量的值,会改变这个闭包的词法环境。然而在一个闭包内对变量的修改,不会影响到另外一个闭包中的变量。
以这种方式使用闭包,提供了许多与面向对象编程相关的好处 —— 特别是数据隐藏和封装。
