绑定到事件时,Javascript闭包的行为会有所不同

时间:2012-02-29 23:45:40

标签: 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 lazyLoadGrid(event, ui)
{
    alert('hi');
}

var test1 = runOnce(lazyLoadGrid);
var test2 = runOnce(lazyLoadGrid);

test1();
test2();

test1();
test2();

它按预期工作 - 'hi'被提醒两次。

但后来我尝试使用runOnce(lazyLoadGrid)作为jQuery UI事件的回调:

$('.accordion').each(function() 
{ 
    $(this).accordion({ autoHeight: false, change: runOnce(lazyLoadGrid) });
});

疯狂随之而来。我期望的是,当首次打开手风琴时,页面上的每个'手风琴'都会运行一次lazyLoadGrid()。相反,闭包回调似乎表现得好像它们都引用了'ran'的相同副本。 lazyLoadGrid()在我第一次打开任何手风琴时运行,然后再也不会为任何其他手风琴再次运行。记录'run'的前置条件值表示每次在第一个之后点击任何手风琴时它都是'true'。

对此有何解释?值得注意的是,我有一个奇怪的页面,嵌套的手风琴,以及多个包含手风琴的jQuery UI标签。更糟糕的是,当我切换标签时,闭合实际上确实在任何给定标签的第一个打开的手风琴上运行。任何建议都非常感谢。

3 个答案:

答案 0 :(得分:1)

怎么样:

function runOnce(fn) {
    return function(){
      fn();
      fn = function(){};
  }
}

// test 
var foo = function(){
    console.log('bar');
}
foo = runOnce(foo);
foo(); // bar
foo();
foo();

答案 1 :(得分:1)

问题:

我相信你遇到的麻烦是因为你所谓的“手风琴”实际上是一个“小组”。手风琴由一组中的所有小组组成。听起来你想要每个面板运行一次,而不是每个手风琴一次。以下演示通过在页面上包含两个手风琴来说明该概念。请注意,lazyLoadGrid()运行两次,每个手风琴一次:

http://jsfiddle.net/cTz4F/

解决方案:

相反,您要做的是创建自定义事件并在每个面板上调用该事件。然后你可以利用jQuery的内置.one()方法,这会导致为每个元素调用一次事件处理程序:

$('.accordion').accordion({
    autoHeight: false,
    change: function(e, ui) {
        ui.newHeader.trigger("activated");
    }
});
$('.accordion > h3').one("activated", lazyLoadGrid);

工作演示: http://jsfiddle.net/cTz4F/1/

答案 2 :(得分:0)

我认为因为函数需要指定event参数。 试试这个:

$('.accordion').each(function() { 
$(this).accordion({ autoHeight: false, change: runOnce(arguments[0],lazyLoadGrid) }); 
});

尝试直接使用函数lazyLoadGrid,或者如果必须使用runOnce,则必须将参数[0](即事件)指定为函数中的参数

- 编辑 -

抱歉,我忘了把事件放在功能