ris*_*ism 22 javascript closures anonymous-function pass-by-reference 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);} );
}
}
Run Code Online (Sandbox Code Playgroud)
我的理论
我要么没有看到一个令人眼花缭乱的语法错误或它的引用问题.最初我有以下语句来设置curTab的值:
curTab=inlineRangeNavUrls[i][0];
Run Code Online (Sandbox Code Playgroud)
因此,当发生问题我计算过,我改变了(通过for循环迭代)的参考curTab,我实际上改变了以前所有的匿名函数事件处理新curTab价值,以及参考....这是为什么事件处理程序始终以最后一个div为目标.
所以我真正需要做的是将curTab 值传递给匿名函数事件处理程序而不是curTab 对象引用.
所以我认为:
curTab=(inlineRangeNavUrls[i][0]).toString();
Run Code Online (Sandbox Code Playgroud)
会解决问题,但事实并非如此.同样的交易.所以很明显我错过了关于这个问题的一些关键,也许是非常基本的知识.谢谢.
Sho*_*og9 23
您需要在循环的每次传递中创建一个新变量,以便它将在您为事件处理程序创建的闭包中捕获.
但是,仅将变量声明移动到循环中将无法实现此目的,因为JavaScript不会为任意块引入新范围.
强制引入新范围的一种简单方法是使用另一个匿名函数:
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
}
}
Run Code Online (Sandbox Code Playgroud)
正如Jason建议的那样,你可以使用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]);
}
Run Code Online (Sandbox Code Playgroud)
Bre*_*ton 17
这里发生的事情是你的无数函数正在形成一个闭包,并将它们的外部范围与它们结合起来.这意味着当你在anomymous函数中引用curTab时,当事件处理程序运行该函数时,它将在你的外部作用域中查找curTab 的当前值.这将是你最后分配给curTab的任何东西.(不是在绑定功能时分配的内容)
你需要做的是改变这个:
$(curDiv).bind("mouseover", function(){showHideRangeSlidingTabs(curTab, true);} );
Run Code Online (Sandbox Code Playgroud)
对此:
$(curDiv).bind("mouseover",
(function (mylocalvariable) {
return function(){
showHideRangeSlidingTabs(mylocalvariable, true);
}
})(curTab)
);
Run Code Online (Sandbox Code Playgroud)
这会将curTab的值复制到外部函数的范围内,内部函数将使用它.这种复制发生在您将内部函数绑定到事件处理程序的同时,因此"mylocalvariable"反映了当时curTab的值.然后下一次循环,将创建一个带有新范围的新外部函数,并将curTab的下一个值复制到其中.
shog9的答案基本上完成了同样的事情,但他的代码更加严谨.
它有点复杂,但如果你想一想就有意义.关闭很奇怪.
编辑:oops,忘了返回内部函数.固定.