js循环添加onclick事件

javascript在循环添加onclick事件时会出现函数传入参数均为最后一个值的问题,编写一个test.html如下所示:

<!doctype html>
<html lang="en">
<head>

</head>
<body>
<input type='button' id='bt0' name='name1' value='0'/>
<input type='button' id='bt1' name='name2' value='1'/>
</body>
<script type='text/javascript'>

for (var i=0;i<2 ;i++)
{
	
	var bt=document.getElementById('bt'+i);
	bt.οnclick=function(){
        console.log(i);//无论点击哪个按钮均打印2
     };
}


</script>
</html>


了解javascript作用域机制的话。很容易看出问题出在哪里。循环结束后,i的作用域为windowscope,或者说i是全局变量,其值为2,click事件触发时显示的自然是2了。
为了解决这个问题,我们从onclick细细讲起

1.当我们点击一个button时,我们做了什么

让我们看看MDN中对onclick的解释

https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onclick

首先,onclick是button的一个属性,我们给这个属性赋值一个函数指针(function pointer),就完成了一个事件绑定,当click事件触发时就会进入这个function。

其次,这个函数指针到底是什么呢?再看看MDN中对onclick后面的function的解释


可以看到这个function既可以是函数声明,也可以是函数表达式。所谓函数表达式和函数声明的区别,举个栗子:var example=function(){}是函数表达式,function example(){}是函数声明。所以这个function有以下三种形式,我们通常用的第一种形式,即匿名函数形式,实际上是一个匿名的函数表达式。

document.getElementById('bt0').click=function(){  //匿名函数
    console.log('example0');
}


var example1=function(){ //函数表达式
    console.log('example1');
};
document.getElementById('bt1').click=example1;

function example2(){ //函数声明
   console.log('example2');
}
document.getElementById('bt2').click=example2;


这是没有参数传递的情况,若有参数呢?直接在函数指针里加参数?elemtent.οnclick=example(parameter)的形式显然不行,因为这是调用函数了,会直接执行。但是可以在example()函数内部返回一个function。

2.在函数表达式或函数声明中返回一个function

点击button后,调用元素对应的onclick事件,解析程序在匿名函数内部找不到i,转而找到外部作用域的i,即为2,所以打印出2。归根到底,是由于element.οnclick=function(){}等式右边的匿名函数无法传参而无法维护自己的作用域。(别想element.οnclick=function(i){}这样传参,因为onclick事件默认的参数是event)。那么解决方法也就很容易想到了,不用匿名函数,而是以函数表达式或函数声明的形式传递参数,这就需要在函数内部返回一个function。

var clickEventExpression=function(arg){
	return function(){
		console.log(arg);
	}
};
function clickEventDeclaration(arg){
	return function(){
		console.log(arg);
	}
}
for (var i=0;i<2 ;i++)
{
	
	var bt=document.getElementById('bt'+i);
	bt.οnclick=clickEventExpression(i);//or bt.οnclick=clickEventDeclaration(i)
}


3.闭包

或者换个思路,仍用匿名函数,不过要在匿名函数内部产生一个可以自己维护的作用域,来保存变量i。咦,好熟悉的概念,这不就是闭包嘛。

“闭包就是将所有自由变量和函数绑定在一个封闭的表达式中,这个表达式可以保留在自由变量和函数创建之外的词法作用域。”这里我们用(function{})()形式的立即执行函数来创建一个封闭的表达式。

//加一层闭包,i 以局部变量形式传递给内层函数
for (var i=0;i<2 ;i++)
{
    (function(){	
        var bt=document.getElementById('bt'+i);
	var temp=i;
        bt.οnclick=function(e){
            console.log(temp);
        }
    })();
}

或者

//加一层闭包,i以函数参数形式传递给内层函数
for (var i=0;i<2 ;i++)
{  
    var bt=document.getElementById('bt'+i);
    bt.οnclick=(function(arg){
        return function(){
            console.log(arg);
            };
    })(i);
}


4.let 和const

let和const的声明可以产生块级作用域,同样可以解决我们的问题。

for (var i=0;i<2 ;i++)
{	
	let ii=i;	//const也可以,块级作用域
	var bt=document.getElementById('bt'+ii);
	bt.οnclick=function(){
		console.log(ii);
	};
}





  • 17
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值