警告不要在循环中执行函数
Posted
技术标签:
【中文标题】警告不要在循环中执行函数【英文标题】:Warning not to make function within a loop 【发布时间】:2018-09-26 12:41:22 【问题描述】:我编写了一个代码来为 div 容器创建模态窗口。单击按钮后,我会获取按钮的编号并显示相关的模式窗口。经过测试,适用于所有浏览器。
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 验证器会给出一个警告“不要在循环中创建函数”。 看了一些与这个主题相关的帖子,特别是函数必须在循环之外创建,我创建了一个函数:
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);
它似乎无法正常工作,它会在网页加载后立即显示最后一个模式窗口。我的理解是该函数必须与单击事件监听器连接,即单击按钮时,应弹出模态窗口。现在,模式窗口弹出,没有任何点击事件。你能给我一个如何正确编写函数的想法吗?或者我是否应该简单地忽略这个 js 验证警告。
【问题讨论】:
为什么要更改函数的内容,为什么现在尝试调用它而不是像以前那样将其安装为处理程序? 【参考方案1】:保持简单!您无需更改任何代码,只需将函数表达式移动到循环体外部的命名函数声明:
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);
【讨论】:
我明白了。实际上,所有答案都是有用的,有助于将解决方案分解成碎片。我明白你的意思了。将代码块移动到函数表达式后,将其作为函数引用添加到事件侦听器中。或者,当处理函数返回自身时,它被用作事件侦听器内部的函数调用。那么,避免污染全局作用域并将所有代码放入 IIFE (function () // code )() 是否有意义? @holler 我不明白您所说的“处理函数返回自身”是什么意思? @holler 是的,将myModalContent
、myBtn
、myHandler
和i
保留在本地范围内是有意义的。【参考方案2】:
警告试图防止“修改后的闭包”出现问题。如果您的函数对变量i
做了任何操作,那么您会发现在用户单击按钮时变量i
的值始终为myBtn.length
,因为这是它最后的值循环。
这个:
for (var i = 0; i < myBtn.length; i++)
...
被这样对待:
var i;
for (i = 0; i < myBtn.length; i++)
...
由于您没有在函数中的任何地方使用i
,因此您在技术上是安全的,但未来其他开发人员可能会更改代码并最终遇到此问题。
为了以您尝试修复它的方式修复此代码,您需要拥有 handler
函数返回一个函数本身。
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
循环内部的,而不是你所在函数的全局范围。
【讨论】:
出于好奇,为什么你必须在createHandler() 函数中返回一个函数。过去,我只编写了函数function openModal() myModalContent.open().../* rest of code here */..
,然后在将函数添加到事件时,只需编写myBtn[i].addEventListener('click', openModal)
。请注意,在 addEventListener 中它没有使用括号,因为它会调用它,在这种情况下您需要返回一个函数。由于我们没有调用它,只是说在点击事件中使用该函数
如果createHandler
不带任何参数,则根本不需要该函数。
警告不是因为闭包可能存在问题,而是因为创建了多个函数,单个对象就足够了。这是性能优化,仅此而已。 (如果有闭包,则需要多个函数,并且不会出现警告)。
@Tyler 和@Bergi,如果不需要根据for
循环中的位置传递参数,您可以只使用一个声明的函数作为处理程序。我正在查看 OP 似乎是如何尝试解决问题的,并解释了为什么他的方法不起作用。以上是关于警告不要在循环中执行函数的主要内容,如果未能解决你的问题,请参考以下文章