警告不要在循环内创建函数

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);
}