欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

javascript中循环添加事件时的闭包问题解决

程序员文章站 2022-03-07 22:21:32
...

这是一个经典问题,只不过有一段时间不写纯js了,最近老是掉进以前跌过的坑,这次花了半天时间爬出来,所以记录一下。

 

昨天写js代码在给自定义的div注册onclick事件时发现得到的值始终是循环里的最后一个层里的值。

最早的代码:

 

for(var i in array){
	var rowDiv = document.createElement("div");
	rowDiv.innerText = array[i]["name"];
	rowDiv.onclick = function(){//注册点击事件
		pNode = this.parentNode;
		pNode.previousSibling.value = this.innerText;//将当前div的值赋给输入框
		if(onclickCallback){//传入了回调函数则执行回调
			onclickCallback(array[i]);
		}
	};
}

 

为了不干扰阅读的视线,将与这个问题无关的代码直接裁掉:

 

for(var i in array){
	var rowDiv = document.createElement("div");
	rowDiv.innerText =array[i];
	rowDiv.onclick = function(){//注册点击事件
		alert(array[i]);
	};
}

 

这里发现点击一个div之后alert出来的始终是最后一次循环的值。这个现象立马想起来这是一个经典的闭包问题,在执行onclick的事件时会向外部函数寻找array[i]的定义,这里的i在for循环执行完之后变成了array[array.length-1]。

 

网上搜了一下,改进的方法有很多种:

方法1,外层包一个匿名函数,将每次循环的索引结果作为入参传入并自执行:

 

for(var i in array){
	(function(obj){
		var rowDiv = document.createElement("div");
		rowDiv.innerText = obj;
		rowDiv.onclick = function(){//注册点击事件
			alert(obj);
		};
	})(array[i]);
}

 

方法2,同样是利用闭包,给innerText赋值部分并没有问题,有问题的只是用到了闭包特性的onclick事件注册部分,所以就改onclick部分:

 

for(var i in array){
	var rowDiv = document.createElement("div");
	rowDiv.innerText = array[i];
	rowDiv.onclick = (function(obj){//注册点击事件
		return function(){
			alert(obj);
		};
	})(array[i]);
}

 

方法3,以上解决翻案的语法看起来挺晦涩,但都是在闭包里打转,个人觉得能不用闭包的地方就尽量别用,一个不小心就会碰上内存泄漏的问题。下面给出另一种解法:

for(var i in array){
	var rowDiv = document.createElement("div");
	rowDiv.innerText = array[i];
	rowDiv.obj = array[i];
	rowDiv.onclick = function(){//注册点击事件
		alert(this.obj);
	};
}

这是我个人认为较好的方案,给dom对象添加自定义属性来保存值,在div的事件触发时可以通过this来引用。