我是 JavaScript 的初学者,我已经阅读了类似主题中的每个答案,但我仍然无法理解到底发生了什么,因为没有人解释我感到困惑的部分。
我有一个包含两个段落的 HTML 文档,这就是我用来将段落颜色更改为 red
的内容。当我点击它时:
var func = function() {
/*Line 1*/ var paragraphs = document.querySelectorAll('p')
/*Line 2*/
/*Line 3*/ for (var i = 0; i < paragraphs.length; i++) {
/*Line 4*/ p = paragraphs[i]
/*Line 5*/ p.addEventListener('click', function() {
/*Line 6*/ p.classList.toggle('red')
/*Line 7*/ })
/*Line 8*/ }
}
func();
结果是,无论我点击哪里,只有最后一段的颜色变为红色。所有与我类似的问题的答案都说在For
之后循环结束,i
的值将是1
,这就是闭包将使用的内容,然后是 eventListener
将添加到相同的第二段中?我可以使用 Immediately-Invoked Function
修复此问题或let
制作i
闭包私有(private),我不知道为什么我应该制作 ì
如果闭包在循环的每次迭代中都可以访问它,则为私有(private)..
我只是不明白这里到底发生了什么,循环不是一行接一行地执行的吗?一开始i
值为 0
,所以在Line 4
变量p
将有第一段,然后在 Line 5-6
该函数将使用 p
并将监听器附加到它,然后第二次执行循环并 i
值为 1
,然后在 Line 5-6
闭包再次获得新值 p
?
我知道闭包可以访问这里的全局变量,例如 i
,因此它可以访问 p
在Line 4
当其值发生变化时。
请问我在这里缺少什么?预先非常感谢您!
最佳答案
您正在展示众所周知的闭包示例......
一开始这可能很难理解
/*Line 1*/ var paragraphs = document.querySelectorAll('p')
/*Line 2*/
/*Line 3*/ for (var i = 0; i < paragraphs.length; i++) {
/*Line 4*/ p = paragraphs[i]
/*Line 5*/ p.addEventListener('click', function() {
/*Line 6*/ p.classList.toggle('red')
/*Line 7*/ })
/*Line 8*/ }
第 5、6 和 7 行包含每个段落中存储的匿名回调函数。该函数依赖于父函数中的变量 i
,因为内部函数使用定义为 paragraphs[i]
的 p
。因此,即使您没有在内部函数中显式使用 i
,您的 p
变量也是如此。假设文档中有 7 个段落...因此,您有 7 个围绕一个 i
变量“封闭”的函数。当父函数终止时,i
不能超出范围,因为 7 个函数需要它。因此,当有人点击其中一个段落时,循环已完成(i
现在将为 8),并且每个函数都会查看相同的 i
> 值。
为了解决这个问题,点击回调函数需要各自获取自己的值,而不是共享一个。这可以通过多种方式完成,但它们都涉及将 i
的副本传递到点击回调函数中,以便将 i
值的副本存储在每个点击回调函数或完全删除 i
的使用。仍然会有一个闭包,但不会出现您最初遇到的副作用,因为嵌套函数不会依赖于父函数中的变量。
下面是一个从嵌套函数中删除 i
的示例,从而解决了问题:
var paragraphs = document.querySelectorAll('p')
for (var i = 0; i < paragraphs.length; i++) {
paragraphs[i].addEventListener('click', function() {
this.classList.toggle('red')
});
}
.red {color:red;}
<p>Paragraph</p>
<p>Paragraph</p>
<p>Paragraph</p>
<p class="red">Paragraph</p>
<p>Paragraph</p>
<p>Paragraph</p>
关于javascript - Javascript 中 for 循环内的闭包结果令人困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40389682/