警告不要在循环内创建函数
Warning not to make function within a loop
我编写了一个代码来为 div 容器创建模态 windows。单击按钮后,我将获得按钮的编号并显示相关的模式 window。经过测试,适用于所有浏览器。
myModalContent = new tingle.modal();
var myBtn = document.querySelectorAll("button.project__btn");
for (var i = 0; i < myBtn.length; i++) {
myBtn[i].addEventListener("click", function () {
myModalContent.open();
if (this.hasAttribute("data-btn")) {
myModalContent.setContent(document.querySelector(".project" + this.getAttribute("data-btn") + "-modal").innerHTML);
} else {
myModalContent.setContent(document.querySelector(".project1-modal").innerHTML);
}
});
}
一个 js 验证器给出了一个警告 "Don't make functions within a loop."
阅读一些与此主题相关的帖子,特别是函数必须在循环外创建,我创建了一个函数:
function handler(modalDiv, trigBtn, index){
modalDiv.open();
if (trigBtn[index].hasAttribute("data-btn")) {
modalDiv.setContent(document.querySelector(".project" + trigBtn[index].getAttribute("data-btn") + "-modal").innerHTML);
} else {
modalDiv.setContent(document.querySelector(".project1-modal").innerHTML);
}
}
然后在循环中调用它:
for (var i = 0; i < myBtn.length; i++) {
myBtn[i].onclick = handler(myModalContent, myBtn, i);
}
它似乎无法正常工作,它在网页加载后立即显示最后一个模式 window。我的理解是该功能必须与点击事件侦听器连接,即当点击按钮时,模态 window 应该弹出。现在,模态 window 弹出而没有任何点击事件。你能告诉我如何正确编写函数吗?或者我是否应该简单地忽略此 js 验证警告。
该警告试图防止 "modified closures" 出现问题。如果您的函数对变量 i
做了任何操作,那么您会发现用户单击按钮时变量 i
的值始终是 myBtn.length
,因为这就是它的值在循环结束时结束。
这个:
for (var i = 0; i < myBtn.length; i++) {
...
是这样对待的:
var i;
for (i = 0; i < myBtn.length; i++) {
...
由于您没有在函数中的任何地方使用 i
,从技术上讲您是安全的,但是将来其他开发人员可能会更改代码并最终将 运行 变成这个问题。
为了按照您尝试修复它的方式修复此代码,您需要具有 handler
函数 return 函数本身。
myBtn[i].addEventListener("click", createHandler());
function createHandler() {
return function() {
myModalContent.open();
if (this.hasAttribute("data-btn")) {
myModalContent.setContent(document.querySelector(".project" + this.getAttribute("data-btn") + "-modal").innerHTML);
} else {
myModalContent.setContent(document.querySelector(".project1-modal").innerHTML);
}
};
}
这与您的工作代码具有相同的效果,但可以防止有人尝试在闭包内使用 i
。如果有人在那里需要 i
,他们可以将它添加到 createHandler
的参数列表中,其中它不会为每次循环重复使用相同的变量。
或者,如果您可以使用 javascript 的现代版本,则可以使用 let
关键字代替 var
。
这个:
for (let i = 0; i < myBtn.length; i++) {
...
更像是此代码在 C# 等语言中的工作方式:
for (var _ = 0; _ < myBtn.length; _++) {
var i = _;
...
换句话说,i
变量的范围在 for
循环内部,而不是您所在函数的全局范围。
保持简单!您不必更改代码的任何内容,只需将函数表达式移动到循环体之外的命名函数声明即可:
var myModalContent = new tingle.modal();
var myBtn = document.querySelectorAll("button.project__btn");
function myHandler() {
myModalContent.open();
if (this.hasAttribute("data-btn")) {
myModalContent.setContent(document.querySelector(".project" + this.getAttribute("data-btn") + "-modal").innerHTML);
} else {
myModalContent.setContent(document.querySelector(".project1-modal").innerHTML);
}
}
for (var i = 0; i < myBtn.length; i++) {
myBtn[i].addEventListener("click", myHandler);
}
我编写了一个代码来为 div 容器创建模态 windows。单击按钮后,我将获得按钮的编号并显示相关的模式 window。经过测试,适用于所有浏览器。
myModalContent = new tingle.modal();
var myBtn = document.querySelectorAll("button.project__btn");
for (var i = 0; i < myBtn.length; i++) {
myBtn[i].addEventListener("click", function () {
myModalContent.open();
if (this.hasAttribute("data-btn")) {
myModalContent.setContent(document.querySelector(".project" + this.getAttribute("data-btn") + "-modal").innerHTML);
} else {
myModalContent.setContent(document.querySelector(".project1-modal").innerHTML);
}
});
} 一个 js 验证器给出了一个警告 "Don't make functions within a loop." 阅读一些与此主题相关的帖子,特别是函数必须在循环外创建,我创建了一个函数:
function handler(modalDiv, trigBtn, index){
modalDiv.open();
if (trigBtn[index].hasAttribute("data-btn")) {
modalDiv.setContent(document.querySelector(".project" + trigBtn[index].getAttribute("data-btn") + "-modal").innerHTML);
} else {
modalDiv.setContent(document.querySelector(".project1-modal").innerHTML);
}
}
然后在循环中调用它:
for (var i = 0; i < myBtn.length; i++) {
myBtn[i].onclick = handler(myModalContent, myBtn, i);
}
它似乎无法正常工作,它在网页加载后立即显示最后一个模式 window。我的理解是该功能必须与点击事件侦听器连接,即当点击按钮时,模态 window 应该弹出。现在,模态 window 弹出而没有任何点击事件。你能告诉我如何正确编写函数吗?或者我是否应该简单地忽略此 js 验证警告。
该警告试图防止 "modified closures" 出现问题。如果您的函数对变量 i
做了任何操作,那么您会发现用户单击按钮时变量 i
的值始终是 myBtn.length
,因为这就是它的值在循环结束时结束。
这个:
for (var i = 0; i < myBtn.length; i++) {
...
是这样对待的:
var i;
for (i = 0; i < myBtn.length; i++) {
...
由于您没有在函数中的任何地方使用 i
,从技术上讲您是安全的,但是将来其他开发人员可能会更改代码并最终将 运行 变成这个问题。
为了按照您尝试修复它的方式修复此代码,您需要具有 handler
函数 return 函数本身。
myBtn[i].addEventListener("click", createHandler());
function createHandler() {
return function() {
myModalContent.open();
if (this.hasAttribute("data-btn")) {
myModalContent.setContent(document.querySelector(".project" + this.getAttribute("data-btn") + "-modal").innerHTML);
} else {
myModalContent.setContent(document.querySelector(".project1-modal").innerHTML);
}
};
}
这与您的工作代码具有相同的效果,但可以防止有人尝试在闭包内使用 i
。如果有人在那里需要 i
,他们可以将它添加到 createHandler
的参数列表中,其中它不会为每次循环重复使用相同的变量。
或者,如果您可以使用 javascript 的现代版本,则可以使用 let
关键字代替 var
。
这个:
for (let i = 0; i < myBtn.length; i++) {
...
更像是此代码在 C# 等语言中的工作方式:
for (var _ = 0; _ < myBtn.length; _++) {
var i = _;
...
换句话说,i
变量的范围在 for
循环内部,而不是您所在函数的全局范围。
保持简单!您不必更改代码的任何内容,只需将函数表达式移动到循环体之外的命名函数声明即可:
var myModalContent = new tingle.modal();
var myBtn = document.querySelectorAll("button.project__btn");
function myHandler() {
myModalContent.open();
if (this.hasAttribute("data-btn")) {
myModalContent.setContent(document.querySelector(".project" + this.getAttribute("data-btn") + "-modal").innerHTML);
} else {
myModalContent.setContent(document.querySelector(".project1-modal").innerHTML);
}
}
for (var i = 0; i < myBtn.length; i++) {
myBtn[i].addEventListener("click", myHandler);
}