如何通过值将变量传递给匿名javascript函数?

时间:2009-07-30 00:45:53

标签: javascript closures pass-by-reference anonymous-function pass-by-value

目标

我想动态地将事件处理程序分配给整个站点的页面上的某些div。

我的方法

我使用jQuery将匿名函数绑定为选定div事件的处理程序。

问题

代码迭代div名称和关联的url数组。 div名称用于设置绑定目标,即将此事件处理程序附加到此div事件。

当事件处理程序成功绑定到每个div事件时,这些事件处理程序触发的操作只会定位到数组中的最后一项。

所以我的想法是,如果用户将鼠标悬停在给定的div上,它应该为该div运行滑出动画。但是,鼠标悬停在div1(rangeTabAll)上会触发div4的滑出动画(rangeTabThm)。对于div 2,3等也是如此。顺序并不重要。更改数组元素,事件将始终以数组中的最后一个元素div4为目标。

我的代码 - (使用jQuery)

var curTab, curDiv;
var inlineRangeNavUrls=[['rangeTabAll','range_all.html'],['rangeTabRem','range_remedial.html'],
                ['rangeTabGym','range_gym.html'],['rangeTabThm','range_thermal.html']];
        for (var i=0;i<inlineRangeNavUrls.length;i++)
        {
            curTab=(inlineRangeNavUrls[i][0]).toString();
            curDiv='#' + curTab;
            if  ($(curDiv).length)
            {
                $(curDiv).bind("mouseover", function(){showHideRangeSlidingTabs(curTab, true);} );
                $(curDiv).bind("mouseout", function(){showHideRangeSlidingTabs(curTab, false);} );
            }
        }

我的理论

我要么没有看到一个令人眼花缭乱的明显语法错误或它的传递参考问题。 最初我有以下语句来设置curTab的值:

curTab=inlineRangeNavUrls[i][0];

所以当出现问题时,我认为当我改变(通过for循环迭代)对curTab的引用时,我实际上将所有以前的匿名函数事件处理程序的引用更改为到新的curTab价值也是......这就是为什么事件处理程序始终以最后一个div为目标的原因。

所以我真正需要做的是将curTab 传递给匿名函数事件处理程序而不是curTab object 引用。

所以我想:

curTab=(inlineRangeNavUrls[i][0]).toString();

会解决问题,但事实并非如此。同样的交易。所以很明显我错过了关于这个问题的一些关键,也许是非常基本的知识。感谢。

4 个答案:

答案 0 :(得分:23)

你需要在循环的每次传递中创建一个新变量,这样它就会被捕获到你为事件处理程序创建的闭包中。

但是,仅将变量声明移动到循环中将无法实现此目的,因为JavaScript doesn't introduce a new scope for arbitrary blocks

强制引入新范围的一种简单方法是使用另一个匿名函数:

for (var i=0;i<inlineRangeNavUrls.length;i++)
{
  curDiv='#' + inlineRangeNavUrls[i][1];
  if ($(curDiv).length)
  {
    (function(curTab)
    {
      $(curDiv).bind("mouseover", function(){showHideRangeSlidingTabs(curTab, true);} );
      $(curDiv).bind("mouseout", function(){showHideRangeSlidingTabs(curTab, false);} );
    })(inlineRangeNavUrls[i][0]); // pass as argument to anonymous function - this will introduce a new scope
  }
}

As Jason suggests,您实际上可以使用jQuery的内置hover()函数清理它:

for (var i=0;i<inlineRangeNavUrls.length;i++)
{
  (function(curTab) // introduce a new scope
  {
  $('#' + inlineRangeNavUrls[i][1])
    .hover(
      function(){showHideRangeSlidingTabs(curTab, true);},
      function(){showHideRangeSlidingTabs(curTab, false);} 
    );
  // establish per-loop variable by passsing as argument to anonymous function
  })(inlineRangeNavUrls[i][0]); 
}

答案 1 :(得分:17)

这里发生的事情是,你的无数函数正在形成一个闭包,并将它们的外部范围与它们结合在一起。这意味着当你在anomymous函数中引用curTab时,当事件处理程序运行该函数时,它将在你的外部作用域中查找curTab的当前值。这将是你最后分配给curTab的任何东西。 (不是在绑定功能时分配的内容)

你需要做的就是改变这个:

$(curDiv).bind("mouseover", function(){showHideRangeSlidingTabs(curTab, true);} );

到此:

$(curDiv).bind("mouseover", 
    (function (mylocalvariable) { 
        return function(){
            showHideRangeSlidingTabs(mylocalvariable, true);
        } 
    })(curTab) 
);

这会将curTab的值复制到外部函数的范围内,而内部函数将使用它。这种复制发生在您将内部函数绑定到事件处理程序的同时,因此“mylocalvariable”反映了当时curTab的值。然后下一次循环,将创建一个带有新范围的新外部函数,并将curTab的下一个值复制到其中。

shog9的答案基本上完成了同样的事情,但他的代码更加严谨。

它有点复杂,但如果你想一想就有意义。关闭很奇怪。

编辑:oops,忘了返回内部函数。固定的。

答案 2 :(得分:1)

我认为你让它变得比它需要的更复杂。如果你正在做的只是在鼠标悬停/分配上分配滑动效果,那么用jquery尝试hover效果。

$("#mytab").hover(function(){
    $(this).next("div").slideDown("fast");},
  function(){
    $(this).next("div").slideUp("fast");
});

如果您发布了完整的HTML,我可以告诉您具体操作方法:)

答案 3 :(得分:1)

您可以将变量的值放入非现有标记中,稍后您可以从那里读取它们。此代码段是循环体的一部分:

 s = introduction.introductions[page * 6 + i][0]; //The variables content
 $('#intro_img_'+i).attr('tag' , s);              //Store them in a tag named tag
 $('#intro_img_'+i).click( function() {introduction.selectTemplate(this, $(this).attr('tag'));}  );  //retrieve the stored data