闭包小实验
阮一峰的博客上有两段代码
片段1
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ return function(){ return this.name; }; } }; alert(object.getNameFunc()()); //The window
片段2
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ var that = this; return function(){ return that.name; }; } }; alert(object.getNameFunc()()); //My Object
不是很理解为什么出来这样的结果,来做个实验。
------------------------------------------------
对于片段1,我们写一个等价的代码
1 var name = "The Window"; 2 var object = { 3 name : "My Object", 4 getNameFunc : function(){ 5 function fn(){ 6 return this.name; 7 } 8 return fn; 9 } 10 }; 11 alert(object.getNameFunc()());
在8行加断点, 显示this指向object
执行到11行的时候,
object.getNameFunc() === function fn(){ return this.name}
这是this指向window了。等于最终把fn拔出来了。看来this这个特殊的关键字并没有闭包的功能
------------------------------------------------------------
对于片段2,写另一个等价的代码
1 var name = "The Window"; 2 var object = { 3 name: "My Object", 4 getNameFunc: function(){ 5 var that = this; 6 function fn(){ 7 return that.name; 8 }; 9 return fn; 10 } 11 }; 12 alert(object.getNameFunc()());
在第5,7,9,12行加断点
1)先跳到12行(函数执行行),此时this指向window,that指向 underfined;
2) 接着跳到5行(object.getNameFunc()),此时环境是
function(){ var that = this; function fn(){ return that.name; }; return fn; }
这个函数,所以this指向object,that也是(实际要到第9行才能看到这样的情况,因为跳到第5行断点时,还没处理这行代码);
3) 接着跳到第9行(return fn === object.getNameFunc()),that和this还是都指向object(还是在这个环境中);
4) 接着跳到7行,此时that指向object,this指向window。因为that在
function(){ var that = this; function fn(){ return that.name; }; return fn; }
环境内,因为return fn的闭包,that被保存在内存中了。
--------------------------------------------------------------------
总之,this是一个特殊的字段,它在不同的函数执行环境下会有不同的指向,并且!!不同的执行环境不能光看在代码中的样子,要看函数执行时它在什么环境里!!!
下面处理面试题,给一串li绑定事件,点击弹出序号。
下面贴一段完美代码
√
1 var ul = document.getElementsByTagName('ul')[0]; 2 var list = ul.getElementsByTagName('li'); 3 for (var i = 0; i < list.length; i++) { 4 var li = list[i]; 5 li.onclick = (function(num) { 6 function fn() { 7 alert(num); 8 } 9 return fn; 10 })(i); 11 }
给第三行加断点,一行一行执行。发先顺序是3,4,5,9,10,5,3,3,4,5,9,10,5, 3,3...
*因为给onclick绑定了一个自执行函数,所以在for循环到onclick的时候会执行(function(){})()里面的内容,有进入fn里
----------------------------------------------------------------------
看下一个有点问题的代码,每次弹出来都是7
✘
1 var ul = document.getElementsByTagName('ul')[0]; 2 var list = ul.getElementsByTagName('li'); 3 for (var i = 0; i < list.length; i++) { 4 var li = list[i]; 5 li.onclick = function() { 6 var num = i; 7 function fn() { 8 alert(num); 9 } 10 return fn(); 11 }; 12 }
给第三行加断点,一行行执行,顺序是3,4,5,3,3,4,5.....
*因为给onclick绑定的只是一个函数,所以到事件发生的时候才会执行,所以在for循环的时候不会跳进return fn那行
-------------------------------------------------------------------------
对两行*有点不太理解? 下面继续做实验
第一段代码相当于这样。for循环的时候就每次都执行了bn函数,所以可以把每次的 i 传入bn里,利用num这个参数用闭包保存住
√
var ul = document.getElementsByTagName('ul')[0]; var list = ul.getElementsByTagName('li'); for (var i = 0; i < list.length; i++) { var li = list[i]; function bn(num) { function fn() { alert(num); } return fn; } li.onclick = bn(i); }
第二段相当于这样,onclick只绑定了bn函数,并没有执行,所以当事件发生执行的之后,i早已变成7了。弹出的也当然是7
✘
var ul = document.getElementsByTagName('ul')[0]; var list = ul.getElementsByTagName('li'); for (var i = 0; i < list.length; i++) { var li = list[i]; function bn() { var num = i; function fn() { alert(num); } return fn(); } li.onclick = bn; }
-----------------------------------------
将第二段小小地改动一下
√
var ul = document.getElementsByTagName('ul')[0]; var list = ul.getElementsByTagName('li'); for (var i = 0; i < list.length; i++) { var li = list[i]; function bn() { var num = i; function fn() { alert(num); } return fn; } li.onclick = bn(); }
和上面那段代码只在倒数两行有差别,可这段代码却可以达到理想的效果,原因也是因为for循环的时候就执行了bn,每次num都用闭包保存住了当前的i值
-------------------------------------------
另一种思路,在绑定事件之前就创建闭包
√
var ul = document.getElementsByTagName('ul')[0]; var list = ul.getElementsByTagName('li'); var arr = []; for (var i = 0; i < list.length; i++) { var li = list[i]; (function(num) { li.onclick = function () { alert(num); }; /*arr.push(li);*/ })(i); } /*for(var i = 0; i < arr.length; i++){ arr[i].onclick = null; }*/
在每次click前就创建了闭包(num就是闭包保存的值,且因为li绑定的function用到了,它会被闭包保存住)。
从这里可以看出,闭包不一定要有return,只要在匿名函数内就可以创建闭包。
----------------------------------------------
关于释放内存
把上段代码注释取消,就是释放闭包的一个方法。(虽然页面上一般不会这样用,绑定完再清空不是发神经吗~)
可ie6要是不释放的话,内存会增加地非常快,根本不能活。
ie6是引用计数,闭包引用了全局里的i。 如果引用了全局中很多变量,即使在外头清理了全局变量,实际上这些变量已经被引用了,根本没有在内存中被删除。

浙公网安备 33010602011771号