Javascript闭包演示

有个网友问了个问题,如下的html,为什么点击所有的段落p输出都是5,而不是alert出对应的0,1,2,3,4。

<!DOCTYPE HTML>
< html >
< head >
< meta charset = "utf-8" />
< title >闭包演示</ title >
< style type = "text/css" >
     p {background:gold;}
</ style >
< script type = "text/javascript" >
function init() {   
     var pAry = 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元素集合,循环给元素添加事件。 在事件响应函数中(event handler)获取对应的索引。但每次获取的都是最后一次循环的索引。

原因是初学者并未理解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() {
   var pAry = 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() {
   var pAry = 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() {
   var pAry = 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() {
   var pAry = 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() {
   var pAry = document.getElementsByTagName_r( "p" );
   for ( var i=0; i<pAry.length; i++ ) {
    pAry[i].onclick = function (arg) {
        return function () { //返回一个函数
        alert(arg);
      }
    }(i) ;
   }

}

后又发现了两种

6、用Function实现,实际上每产生一个函数实例就会产生一个闭包

function init6() {
     var pAry = 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() {
     var pAry = 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(”圆心”) 这个对象,而 alert yx01 则显示 “圆心”。

情景二:

var yx02 = function() {return "圆心"}();
alert(yx02);我们运行情景二代码,将返回显示“圆心”,此时该代码等价于:

var 匿名函数 = function() {return "圆心"};
yx02 = 匿名函数();
alert(yx02);很明显,yx02 返回的是匿名函数的执行结果值,即 yx02 为:“圆心”。

当然匿名函数的执行结果也可以为一个匿名对象。具体常见应用可以看《Javascript的一种模块模式

你可能感兴趣的:(JavaScript)