深入理解JavaScript的闭包特性

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

   
   
   
   
1. DOCTYPE HTML >
2.
< html >
3.
< head >
4.
< meta charset ="utf-8" />
5.
< title > 闭包演示 title >
6.
< style type ="text/css" >
7. p
{ background : gold ; }
8.
style >
9.
< script type ="text/javascript" >
10 . function init() {
11 . var pAry = document.getElementsByTagName( " p " );
12 . for ( var i = 0 ; i < pAry.length; i ++ ) {
13 . pAry[i].onclick = function () {
14 . alert(i);
15 . }
16 . }
17 . }
18 . script >
19.
head >
20.
< body onload ="init();" >
21.
< p > 产品 0 p >
22.
< p > 产品 1 p >
23.
< p > 产品 2 p >
24.
< p > 产品 3 p >
25.
< p > 产品 4 p >
26.
body >
27.
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)上

   
   
   
   
1 . function init1() {
2 . var pAry = document.getElementsByTagName( " p " );
3 . for ( var i = 0 ; i < pAry.length; i ++ ) {
4 . pAry[i].i = i;
5 . pAry[i].onclick = function () {
6 . alert( this .i);
7 . }
8 . }
9 . }

  2、将变量 i 保存在匿名函数自身

   
   
   
   
1 . function init2() {
2 . var pAry = document.getElementsByTagName( " p " );
3 . for ( var i = 0 ; i < pAry.length; i ++ ) {
4 . (pAry[i].onclick = function () {
5 . alert(arguments.callee.i);
6 . }).i = i;
7 . }
8 . }

  后又想到了三种

  3、加一层闭包,i 以函数参数形式传递给内层函数

   
   
   
   
1 . function init3() {
2 . var pAry = document.getElementsByTagName( " p " );
3 . for ( var i = 0 ; i < pAry.length; i ++ ) {
4 . ( function (arg){
5 . pAry[i].onclick = function () {
6 . alert(arg);
7 . };
8 . })(i); // 调用时参数
9 . }
10 . }

  4、加一层闭包,i 以局部变量形式传递给内层函数

   
   
   
   
1 . function init4() {
2 . var pAry = document.getElementsByTagName( " p " );
3 . for ( var i = 0 ; i < pAry.length; i ++ ) {
4 . ( function () {
5 . var temp = i; // 调用时局部变量
6 . pAry[i].onclick = function () {
7 . alert(temp);
8 . }
9 . })();
10 . }
11 . }

  5、加一层闭包,返回一个函数作为响应事件(注意与3的细微区别)

   
   
   
   
1 . function init5() {
2 . var pAry = document.getElementsByTagName( " p " );
3 . for ( var i = 0 ; i < pAry.length; i ++ ) {
4 . pAry[i].onclick = function (arg) {
5 . return function () { // 返回一个函数
6 . alert(arg);
7 . }
8 . }(i);
9 . }
10 . }

  后又发现了两种

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

   
   
   
   
1 . function init6() {
2 . var pAry = document.getElementsByTagName( " p " );
3 . for ( var i = 0 ; i < pAry.length; i ++ ) {
4 . pAry[i].onclick = new Function( " alert( " + i + " ); " ); // new一次就产生一个函数实例
5 . }
6 . }

  7、用Function实现,注意与6的区别

   
   
   
   
1 function init7() {
2 var pAry = document.getElementsByTagName( " p " );
3 for ( var i = 0 ; i < pAry.length; i ++ ) {
4 pAry[i].onclick = Function( ' alert( ' + i + ' ) ' );
5 }
6 }

你可能感兴趣的:(JavaScript,闭包,Web)