绑定到事件时,Javascript闭包的行为不同
我试图使用闭包来确保函数只能执行一次。听起来很简单,工作原理如下:绑定到事件时,Javascript闭包的行为不同,javascript,jquery,jquery-ui,closures,Javascript,Jquery,Jquery Ui,Closures,我试图使用闭包来确保函数只能执行一次。听起来很简单,工作原理如下: function runOnce(fn) // returns copy of fn which can only execute once { var ran = false; return function() { if (!ran) { fn(); ran = true; } }; } 我
function runOnce(fn) // returns copy of fn which can only execute once
{
var ran = false;
return function()
{
if (!ran)
{
fn();
ran = true;
}
};
}
我已经测试了如下功能:
function lazyLoadGrid(event, ui)
{
alert('hi');
}
var test1 = runOnce(lazyLoadGrid);
var test2 = runOnce(lazyLoadGrid);
test1();
test2();
test1();
test2();
它的工作原理与预期一样——“嗨”准确地被提醒了两次
但是我尝试使用runOnce(lazyLoadGrid)作为jQuery UI事件的回调:
$('.accordion').each(function()
{
$(this).accordion({ autoHeight: false, change: runOnce(lazyLoadGrid) });
});
疯狂随之而来。我所期望的是,页面上的每个“accordion”在第一次打开该accordion时,将只运行lazyLoadGrid()一次。相反,闭包回调的行为好像它们都引用了“ran”的同一个副本。lazyLoadGrid()在我第一次打开任何手风琴时运行,然后再也不会为任何其他手风琴运行。记录“ran”的前置条件值表明,每当我单击第一个手风琴后的任何一个手风琴时,它都是“true”
对此有何解释?值得注意的是,我有一个奇怪的页面,带有嵌套的accordios,还有多个jqueryui选项卡,每个选项卡都包含accordios。更糟糕的是,当我切换选项卡时,闭包实际上在任何给定选项卡的第一个打开的手风琴上运行。非常感谢您的建议。我认为这是因为函数希望指定事件参数。 试试这个:
$('.accordion').each(function() {
$(this).accordion({ autoHeight: false, change: runOnce(arguments[0],lazyLoadGrid) });
});
尝试直接使用函数lazyLoadGrid,或者如果必须使用runOnce,则必须将参数[0](即事件)指定为函数中的参数
--编辑--
对不起,我忘了将事件放入函数中如何:
function runOnce(fn) {
return function(){
fn();
fn = function(){};
}
}
// test
var foo = function(){
console.log('bar');
}
foo = runOnce(foo);
foo(); // bar
foo();
foo();
问题是:
我相信你遇到的麻烦是因为你认为的“手风琴”实际上是一个“面板”。手风琴由一组中的所有面板组成。听起来你想在每个面板上运行一次,而不是在每个手风琴上运行一次。下面的演示通过在一个页面上包含两个手风琴来说明这个概念。请注意,lazyLoadGrid()
运行两次,每个手风琴运行一次:
解决方案:
相反,您要做的是创建一个自定义事件并在每个面板上调用该事件。然后,您可以利用jQuery的内置.one()
方法,该方法会使每个元素只调用一次事件处理程序:
$('.accordion').accordion({
autoHeight: false,
change: function(e, ui) {
ui.newHeader.trigger("activated");
}
});
$('.accordion > h3').one("activated", lazyLoadGrid);
工作演示:我无法在此处复制它:。这两个ID只记录一次,并且彼此独立。你确定你在实际代码中使用了
每个
?只想说,这是一个写得很好的问题。你能发布一个链接到一个页面吗?最好是一个JSFIDLE,它演示了这个问题?Felix,我直接从我的实际代码粘贴了上面的引用。缺少一些上下文,例如所有引用的代码(函数定义除外)都在$.ready()内运行。我不确定这是否相关。OP的代码有什么不同?如果您不打算使用某个函数,则永远不必为该函数指定参数。对于他正在执行的操作,您必须指定事件,因为它不会在每次调用该函数时都是相同的。这对我来说仍然没有多大意义。在本例中,arguments[0]
引用传递给每个
的第一个参数,该参数是集合中元素的索引。这里没有event对象,为什么runOnce
仍然需要它?我使用参数[0]作为调用函数的事件。当您在运行时将函数设置为onblur()或onchange()时,您必须在函数中指定事件,就像您要用onchange()编码按钮一样:单击按钮是函数onchange的事件。是的,但您不必指定它,只要您想使用它。正如我所说,参数[0]
没有引用事件对象,因为事件处理程序不是当前执行的。执行each
的回调,并且each
获取索引和元素作为参数()。如果有,您必须定义lazyLoadGrid
来接受参数。很抱歉,你的答案是错的。这也行得通,但没有理由OPs代码不起作用,@pimvdb在他的演示中显示它确实起作用。因此,问题可能不在于问题中的代码,而在于其他地方。这不起作用,因为覆盖变量不会覆盖任何对象中的任何属性。@pimvdb-Wait。什么?为什么这样不行?将演示中的实现替换为此实现的效果与此相同:。@gilly3:Woops。我以为foo
被覆盖了。没关系:)可悲的是,事情就是这样。