修复循环中的闭包
Fixing a closure within a loop
我正在阅读 David Flanagan 的 JavaScript:权威指南,我被困在以下示例中:
window.onload = function() {
var elements = document.getElementsByClassName("reveal");
for(var i = 0; i < elements.length; i++) {
var elt = elements[i];
var title = elt.getElementsByClassName("handle")[0];
title.onclick = function() {
if(elt.className == "reveal") elt.className = "revealed";
else if(elt.className == "revealed") elt.className = "reveal;"
}
}
};
<div class="reveal">
<h1 class="handle">Click Here to Reveal Hidden Text</h1>
<p>This paragraph is hidden. It appears when you click on the title.</p>
</div>
问题是当前形式的代码可以工作,但是如果我用 reveal
的 class 添加多个 div
,无论 h1
我点击的元素,只有最后一个会展开。我应该在代码中更改什么才能使其在单独的 div 上工作?
P.S。我确实知道由于问题的性质是循环内的闭包,所以经常会问这种问题,但是鉴于 SO 上类似问题的解决方案,我无法使我的代码正常工作。谢谢。
在JavaScript中,变量范围设置在函数级别。函数创建 closures 本质上定义了范围的边界。 子作用域可以访问定义在父作用域中的变量,但反之则不行。
当您调用 onclick
处理程序函数时,它必须查找 up 作用域链 以找到变量 elt
因为 elt
没有在当前范围内声明。
范围链中的下一级是您的 window.onload
处理函数(即闭包)。在此范围内,变量 elt
是 声明的,它的值由循环设置。 但请记住,当您单击 <h1>
并调用 onclick
处理程序时,循环已经终止。因此,elt
将始终是循环中设置的最后一个值。这就是为什么您总是将 elt
作为最后一个元素。
要解决此问题,您可以使用立即执行函数。立即执行的函数创建一个新的闭包(和范围),其中 elt
在本地声明。因为变量 elt
现在是在这个新的内部作用域中声明的,所以它不会被父作用域中的循环修改:
window.onload = function() {
var elements = document.getElementsByClassName("reveal");
for(var i = 0; i < elements.length; i++) {
var elt = elements[i];
var title = elt.getElementsByClassName("handle")[0];
title.onclick = (function(elt) {
return function() {
if(elt.className == "reveal") elt.className = "revealed";
else if(elt.className == "revealed") elt.className = "reveal;"
};
})(elt);
}
};
我正在阅读 David Flanagan 的 JavaScript:权威指南,我被困在以下示例中:
window.onload = function() {
var elements = document.getElementsByClassName("reveal");
for(var i = 0; i < elements.length; i++) {
var elt = elements[i];
var title = elt.getElementsByClassName("handle")[0];
title.onclick = function() {
if(elt.className == "reveal") elt.className = "revealed";
else if(elt.className == "revealed") elt.className = "reveal;"
}
}
};
<div class="reveal">
<h1 class="handle">Click Here to Reveal Hidden Text</h1>
<p>This paragraph is hidden. It appears when you click on the title.</p>
</div>
问题是当前形式的代码可以工作,但是如果我用 reveal
的 class 添加多个 div
,无论 h1
我点击的元素,只有最后一个会展开。我应该在代码中更改什么才能使其在单独的 div 上工作?
P.S。我确实知道由于问题的性质是循环内的闭包,所以经常会问这种问题,但是鉴于 SO 上类似问题的解决方案,我无法使我的代码正常工作。谢谢。
在JavaScript中,变量范围设置在函数级别。函数创建 closures 本质上定义了范围的边界。 子作用域可以访问定义在父作用域中的变量,但反之则不行。
当您调用 onclick
处理程序函数时,它必须查找 up 作用域链 以找到变量 elt
因为 elt
没有在当前范围内声明。
范围链中的下一级是您的 window.onload
处理函数(即闭包)。在此范围内,变量 elt
是 声明的,它的值由循环设置。 但请记住,当您单击 <h1>
并调用 onclick
处理程序时,循环已经终止。因此,elt
将始终是循环中设置的最后一个值。这就是为什么您总是将 elt
作为最后一个元素。
要解决此问题,您可以使用立即执行函数。立即执行的函数创建一个新的闭包(和范围),其中 elt
在本地声明。因为变量 elt
现在是在这个新的内部作用域中声明的,所以它不会被父作用域中的循环修改:
window.onload = function() {
var elements = document.getElementsByClassName("reveal");
for(var i = 0; i < elements.length; i++) {
var elt = elements[i];
var title = elt.getElementsByClassName("handle")[0];
title.onclick = (function(elt) {
return function() {
if(elt.className == "reveal") elt.className = "revealed";
else if(elt.className == "revealed") elt.className = "reveal;"
};
})(elt);
}
};