绑定到事件时,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
被覆盖了。没关系:)可悲的是,事情就是这样。