这里的这个例子是引用于《JavaScript高级程序设计-第2版》的第4章-函数里的例子,结合对汤姆大叔博客里对函数的理解,需要对函数的构建及执行过程作以细致的分析理解,才能对闭包作以深入的分析与应用。以下是我对闭包的理解——
1 (function($){ 2 3 function createFunction() { 4 var result = new Array(); 5 for (var i = 0; i < 3; i++) { 6 /** 7 * 当执行到下面这句代码的时候,每创建一个函数,就对应创建了一个对象,这个函数对象的[[scope]]属性里包括了该执行环境的VO和AO 8 * 而且这里的VO和AO充当了下面这个函数的公共作用域。 9 */ 10 result[i] = function() { 11 /** 12 * 1>这个函数作用域可以访问另一个函数的作用域,那必然就是闭包了 13 * 2>这个闭包返回了数组成员对象,会导致这里的i一直存在于内存中 14 * 3>这里能够引出一个公共作用域的概念: 15 * a>这个函数的[[scope]] = [ 16 * createFunctionContext.AO, 17 * 匿名函数Context.AO, 18 * globalContext.VO 19 * ] 20 * b>当去检索i变量的时候,在第一层父级执行环境createFunctionContext.AO中就能够找到,而对于此时的i变量时一直存在于内存中的, 21 * 而且其值一直是3 22 * c>也就是说,这里的公共作用域里的变量正是这个函数的[[scope]](父级作用域链)属性里的值 23 */ 24 return i; 25 } 26 } 27 return result; 28 } 29 /** 30 * 当这个函数执行完成之后,这个函数的作用域链会被销毁,但是这个函数的活动对象并不销毁,仍然留在内存中,除非这里的funs变量被销毁, 31 * 才会释放这个函数里的i变量所占据的空间 32 */ 33 var funs = createFunction(); 34 for (var i = 0; i < funs.length; i++) { 35 document.write(funs[i]() + ','); //3,3,3, 36 } 37 38 //利用闭包保存状态 39 function createFunction() { 40 var result = new Array(); 41 for (var i = 0; i < 3; i++) { 42 //第一种改造 43 (function(num) { //每一次的循环都是一个函数的创建过程,当前这个函数的AO对象里面都保存了num变量 44 /** 45 * 为什么经过这个改造,它的打印结果就是0,1,2,了呢?一定要从作用域链的角度去理解,这样理解才是最为透彻的 46 * 这个匿名函数的Scope = AO + [[scope]],随着循环的进行,相当于创建了三个这样的匿名函数, 47 * 传入的这个num参数正是当前Scope里的AO里面的值,由于基本类型变量的传值是按值传递的,所以这里的num记录了 48 * 每一次i的值并存入进当前AO里,因此当下面的这个匿名函数在执行时,访问的num值均是这里的AO对象里的num值。 49 */ 50 result[num] = function() { 51 /** 52 * 这里的num指代的是第一层closure里的num值,这里的num变量会一直存在于内存当中, 53 * 除非重新赋值这里的result[num]为null,也就是说随着匿名函数的消失,它对应的作用域链和变量才会消失。 54 */ 55 return num; // 56 } 57 //尽管这里的num变量一直存在于内存当中,但这里的num变量仅仅是当前函数的局部变量,该匿名函数的外部是访问不到num的 58 })(i); //当这里的匿名函数执行完成后,这里的num变量不会被立即被销毁,源于里面的匿名函数引用了num变量 59 60 //第二种改造 61 result[i] = function(num) { //和上面是一样的,每一次均创建函数并赋值给数组成员,对应的[[scope]]属性值相同, 62 //但是活动对象AO却不同,保存着不同的num值 63 return function() { 64 //当进入该执行环境时,返回的是不同的第一层closure的num值 65 return num; 66 } 67 }(i); 68 69 } //当执行完这句代码的时候,这里的i变量就彻底没了 70 return result; 71 } 72 73 })(jQuery);
闭包是JavaScript函数应用的核心,深入理解了闭包,就能解释很多怪异的函数执行现象,当然,也能构造出非常巧妙的设计。
还有一点要注意,之前在网上看多了很多有关闭包的说法,他们对闭包的理解真是五花八门,对此,我的建议是——一定要从作用域链(或者可以理解为标识符解析)的角度去思考理解闭包。
注:随时欢迎任何博友对我的理解有误之处作以评论和更正。