Javascript闭包演示
有个网友问了个问题,如下的html,为什么点击所有的段落p输出都是5,而不是alert出对应的0,1,2,3,4。
<!DOCTYPEHTML><html><head><meta charset="utf-8"/><title>闭包演示</title><style type="text/css">????p{background:gold;}</style><script type="text/javascript">function init(){???????varpAry =document.getElementsByTagName_r("p");???????for(var i=0; i<pAry.length; i++ ){????????????pAry[i].onclick= function(){????????????alert(i);???????}??}}</script></head><body onload="init();"><p>产品0</p><p>产品1</p><p>产品2</p><p>产品3</p><p>产品4</p></body></html>
以上场景是初学者经常碰到的。即获取HTML元素集合,循环给元素添加事件。在事件响应函数中(eventhandler)获取对应的索引。但每次获取的都是最后一次循环的索引。原因是初学者并未理解JavaScript的闭包特性。通过element.onclick=function(){alert(i);}方式给元素添加点击事件。响应函数function(){alert(i);}中的i 并非每次循环时对应的 i(如0,1,2,3,4)而是循环后最后 i 的值5。 或者说循环时响应函数内并未能保存对应的值i,而是最后一次i++的值5。
了解了原因,摸索出了很多解决办法(纯粹是兴趣)。最先想到的前两种
1、将变量 i 保存给在每个段落对象(p)上
unction init1() {??varpAry =document.getElementsByTagName_r("p");??for(var i=0; i<pAry.length; i++ ){?????pAry[i].i = i;?????pAry[i].onclick= function() {????????alert(this.i);?????}??}}2、将变量 i 保存在匿名函数自身
function init2() {??varpAry =document.getElementsByTagName_r("p");??for(var i=0; i<pAry.length; i++ ){???(pAry[i].onclick = function() {????????alert(arguments.callee.i);????}).i= i;??}}后又想到了三种
3、加一层闭包,i 以函数参数形式传递给内层函数
function init3() {??varpAry =document.getElementsByTagName_r("p");??for(var i=0; i<pAry.length; i++ ){???(function(arg){???????pAry[i].onclick= function() {??????????alert(arg);???????};???})(i);//调用时参数??}}4、加一层闭包,i 以局部变量形式传递给内层函数
function init4() {??varpAry =document.getElementsByTagName_r("p");??for(var i=0; i<pAry.length; i++ ){????(function() {??????var temp =i;//调用时局部变量??????pAry[i].onclick= function() {????????alert(temp);??????}????})();??}}5、加一层闭包,返回一个函数作为响应事件(注意与3的细微区别)
?
function init5() {??varpAry =document.getElementsByTagName_r("p");??for(var i=0; i<pAry.length; i++ ){???pAry[i].onclick = function(arg) {???????returnfunction() {//返回一个函数???????alert(arg);?????}???}(i);??}}
后又发现了两种
6、用Function实现,实际上每产生一个函数实例就会产生一个闭包
function init6() {????varpAry =document.getElementsByTagName_r("p");????for(var i=0; i<pAry.length; i++ ){??????pAry[i].onclick= new Function("alert(" + i + ");");//new一次就产生一个函数实例????}}7、用Function实现,注意与6的区别
function init7() {????varpAry =document.getElementsByTagName_r("p");????for(var i=0; i<pAry.length; i++ ){?????????pAry[i].onclick= Function('alert('+i+')');????}}New Function() 与Function()的区别:
情景一:
var yx01 = new function() {return "圆心"};
alert(yx01);
我们运行情景一代码,将返回显示“[object object]”,此时该代码等价于:
function 匿名类(){
return "圆心";
}
var yx01 = new 匿名类();
alert(yx01);我们对情景一的代码进行下面改造:
var yx01 = new function() {return new String("圆心")};
alert(yx01);
我们运行,将会发现返回的是“圆心”,这是为什么呢?
只 要 new表达式之后的 constructor返回(return)一个引用对象(数组,对象,函数等),都将覆盖new创建的匿名对象,如果返回(return)一个原始类型(无return 时其实为 return 原始类型 undefined),那么就返回 new 创建的匿名对象
由于 new String 会构造一个对象,而不是一个 string 直接量,且new String(x)如果带参数,那么alert它的时候就会返回 x。所以 yx01 将返回 new String(”圆心”) 这个对象,而 alertyx01 则显示 “圆心”。
情景二:
var yx02 = function() {return "圆心"}();
alert(yx02);我们运行情景二代码,将返回显示“圆心”,此时该代码等价于:
var 匿名函数 =function() {return "圆心"};
yx02 =匿名函数();
alert(yx02);很明显,yx02 返回的是匿名函数的执行结果值,即 yx02 为:“圆心”。
当然匿名函数的执行结果也可以为一个匿名对象。具体常见应用可以看《Javascript的一种模块模式》