深入理解JavaScript的闭包特性

摘要:初学者经常碰到的,即获取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.οnclick=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 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值