案例:出现产品条,当单击第几行产品,出现对应编号
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaScript闭包属性详解</title>
<style type="text/css">
p {
background: gold;
}
</style>
<script type="text/javascript">
function init() {
var pAry = document.getElementsByTagName('p');
for (var i = 0; i < pAry.length; i++) {
pAry[i].onclick = function() {
alert(i);
}
}
}
</script>
</head>
<body onload="init();">
<p>产品0</p>
<p>产品1</p>
<p>产品2</p>
<p>产品3</p>
<p>产品4</p>
</body>
</html>
-
问题:无论单击哪个div,都会弹出5。
-
原因:onclick事件是异步触发的,当事件被触发时,for循环早已结束,此时变量i的值早已经是5。
-
解决:在闭包的帮助下,把每次循环的i值都封闭起来。当事件函数顺着作用域链从内到外查找变量i时,会先找到被封闭在闭包环境的i,单击div时,会分别输出0,1,2,3,4。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaScript闭包属性详解</title>
<style type="text/css">
p {
background: gold;
}
</style>
<script type="text/javascript">
function init() {
var pAry = document.getElementsByTagName('p');
for (var i = 0; i < pAry.length; i++) {
function outer(n) {
var m = n;
return function inner() {
alert(m);
}
}
pAry[i].onclick = outer(i);
}
}
</script>
</head>
<body onload="init();">
<p>产品0</p>
<p>产品1</p>
<p>产品2</p>
<p>产品3</p>
<p>产品4</p>
</body>
</html>
更经典和简单的解决办法是使用立即执行函数。
为什么这样就行了呢,function()自己调用了自己,这时候onclick引用的变量变成了n,而由于立即执行函数的原因,每个onclick函数在作用域链中分别保持着对应的n(0~length-1),这时候就可以了。
补充:立即执行函数
立即执行函数案例
(function(a, b){ // 形参
console.log(a + b); // 3
}(1, 2)); // 实参
此题对应
(function(n){//n是形参
pAry[n].onclick = function() {
alert(n);
}
})(i);//i是实参
所以利用立即执行函数,来实现外部调用内部变量。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaScript闭包属性详解</title>
<style type="text/css">
p {
background: gold;
}
</style>
<script type="text/javascript">
function init() {
var pAry = document.getElementsByTagName('p');
for(var i=0; i<pAry.length; i++) {
(function(n){
pAry[n].onclick = function() {
alert(n);
}
})(i);
}
}
</script>
</head>
<body onload="init();">
<p>产品0</p>
<p>产品1</p>
<p>产品2</p>
<p>产品3</p>
<p>产品4</p>
</body>
</html>
运行后,在网页下按F12中。打开source选项
在第14行前面点击添加断点,可以观察到作用域链
代码在第14行和15行之间来回跳转,因为没有单击事件发生,所以没有alert(i)事件发生。
直到i=5,init()函数执行完成。
最简单的方法:把var改成let
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaScript闭包属性详解</title>
<style type="text/css">
p {
background: gold;
}
</style>
<script type="text/javascript">
function init() {
var pAry = document.getElementsByTagName('p');
for (let i = 0; i < pAry.length; i++) {
pAry[i].onclick = function() {
alert(i);
}
}
}
</script>
</head>
<body onload="init();">
<p>产品0</p>
<p>产品1</p>
<p>产品2</p>
<p>产品3</p>
<p>产品4</p>
</body>
</html>