如何给循环中的对象添加事件--深入理解JavaScript的闭包特性

时间:2023-03-08 17:04:52

初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件。在事件响应函数中(event handler)获取对应的索引。但每次获取的都是最后一次循环的索引。原因是初学者并未理解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闭包的特性

2009-07-24 17:30 司徒正美 cnblogs 我要评论(1) 字号:T | T

本文将对Javascript闭包的特性进行分析,并举例进行说明。闭包,是指语法域位于某个特定的区域,具有持续参照(读写)位于该区域内自身范围之外的执行域上的非持久型变量值能力的段落。

AD:

Javascript闭包的定义非常晦涩——闭包,是指语法域位于某个特定的区域,具有持续参照(读写)位于该区域内自身范围之外的执行域上的非持久型变量值能力的段落。这些外部执行域的非持久型变量神奇地保留它们在闭包最初定义(或创建)时的值(深连结)。

简单来说,Javascript闭包就是在另一个作用域中保存了一份它从上一级函数或作用域取得的变量(键值对),而这些键值对是不会随上一级函数的执行完成而销毁。周爱民说得更清楚,闭包就是“属性表”,闭包就是一个数据块,闭包就是一个存放着“Name=Value”的对照表。就这么简单。但是,必须强调,闭包是运行期概念,一个函数实例。

Javascript闭包的实现,通常是在函数内部再定义函数,让该内部函数使用上一级函数的变量或全局变量。

ECMAScript认为使用全局变量是一个简单的Javascript闭包实例。

1.  var sMessage = "Hello World";

2.  function sayHelloWorld(){

3.  alert(sMessage);

4.  };

5.  sayHelloWorld();

但它完成没有体现Javascript闭包的特性……

现在比较让人认同的Javascript闭包实现有如下三种

1.  with(obj){

2.  //这里是对象闭包

3.  }(function(){

4.  //函数闭包

5.  })()try{

6.  //...

7.  } catch(e) {

8.  //catch闭包 但IE里不行

9.  }

附上今天在无忧看到的问题:

要求:

让这三个节点的Onclick事件都能正确的弹出相应的参数。

1.  <ul>

2.  <li id="a1">aa</li>

3.  <li id="a2">aa</li>

4.  <li id="a3">aa</li>

5.  </ul>

6.  <script type="text/javascript">

7.  <ul>

8.  <li id="a1">aa</li>

9.  <li id="a2">aa</li>

10. <li id="a3">aa</li>

11. </ul>

12. <script type="text/javascript">

13. for(var i=1; i < 4; i++){

14. var id = document.getElementById("a" + i);

15. id.onclick = function(){

16. alert(i);//现在都是返回4

17. }

18. }

19. </script>

客服果果的解答:

1.  for(var i=1; i < 4; i++){

2.  var id = document.getElementById("a" + i);

3.  /*

4.  这里生成了一个匿名函数并赋值给对象 id_i;

5.  */

6.  id.onclick = function(){

7.  /*

8.  这个i来源于局部变量,无法以window.i或者obj.i的形式在后期引用,

9.  只好以指针或者变量地址方式保存在这个匿名函数中,

10. 这就是传说的闭包,所以所有这个过程中生成的事件句柄都使用引用

11. 的方式来持久这个变量,也就是这些匿名函数共用一个变量i;

12. */

13. alert(i);

14. };

15. };

局部变全局

1.  for(var i=1; i < 4; i++){

2.  var id = document.getElementById("a" + i);

3.  id.i=i;//这个i有了根

4.  id.onclick=function(){

5.  alert(this.i)

6.  };

7.  };1.for(var i=1; i < 4; i++){

8.  var id = document.getElementById("a" + i);

9.  window[id.id]=i;//这个i有了根

10. id.onclick=function(){

11. alert(window[this.id]);

12. };

13. }

产生一对一的更多Javascript闭包

1.  for(var i=1; i < 4; i++){

2.  var id = document.getElementById("a" + i);

3.  id.onclick = new function(){

4.  var i2=i;//这个i是闭包的闭包

5.  return function(){

6.  alert(i2);

7.  }

8.  };

9.  }