我是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私有于闭包,我不知道如果闭包在循环的每一次迭代中都有访问权限,为什么要使ì成为私有的。
我只是不明白这里到底发生了什么,循环不是一个接一个地执行的吗?在开始时,i将有一个值0,因此在Line 4变量p将有第一段,然后在Line 5-6,函数将使用该p并将侦听器附加到它,然后循环第二次执行,i将有一个值1,然后在Line 5-6,闭包再次得到p的新值?
我知道闭包在这里可以访问全局变量,比如i,所以当它的值发生变化时,它可以访问p at Line 4。
我在这里少了什么?非常感谢您提前!
发布于 2016-11-02 21:07:27
你展示的是谚语的闭包例子..。
这在一开始很难理解。
/*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段。正因为如此,在一个i变量周围有7个“封闭”函数。当父函数终止时,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>
发布于 2016-11-02 21:01:24
闭包是在变量的值中关闭的函数,因此它们在循环的下一次迭代中不会发生变化,这发生在click发生之前,等等。
var paragraphs = document.querySelectorAll('p');
for (var i = 0; i < paragraphs.length; i++) {
(function(p) { // <- any function call, would create a new scope, and hence a closure
p.addEventListener('click', function() {
p.classList.toggle('red'); // note that "this" would work here, instead of "p"
});
})(paragraphs[i]); // <- in this case, it's an IIFE, but it doesn't have to be
}那就结束了
发布于 2016-11-02 21:04:39
在JavaScript (ECMA-Script5和更早版本)中,只有函数才能创建作用域。
另一方面,闭包不捕获变量值。也就是说,正如您已经说过的那样,您需要自己使用IIFE(立即调用的函数表达式):
for (var i = 0; i < paragraphs.length; i++) {
(function(p) {
p.addEventListener('click', function() {
p.classList.toggle('red')
})
})(paragraphs[i]);
}顺便说一句,谁知道您是否可以使用document.querySelectorAll简化这段代码
// Now you don't need IIFEs anymore...
// Change the "p" selector with whatever CSS selector
// that might fit better in your scenario...
Array.from(document.querySelectorAll("p"))
.forEach(function(p) {
p.addEventListener("click", function() {
p.classList.toggle('red');
});
});实际上,您可以重构代码以使用Array.prototype.forEach,也不需要使用IIFE:
paragraphs.forEach(function(p) {
p.addEventListener("click", function() {
p.classList.toggle('red');
});
});https://stackoverflow.com/questions/40389682
复制相似问题