【问题标题】:Scope troubles in Javascript when passing an anonymous function to a named function with a local variable将匿名函数传递给具有局部变量的命名函数时,Javascript 中的范围问题
【发布时间】:2011-02-17 03:52:08
【问题描述】:

抱歉标题 - 我想不出办法来表达它。

这是场景:

我有一个构建元素的函数:

buildSelect(id,cbFunc,...)

在 buildSelect 里面是这样的:

select.attachEvent('onchange',cbFunc);

我还有一个数组:

var xs = ['x1','x2','x3'...];

鉴于所有这些,我有一些代码可以做到这一点:

for(var i = 0; i < xs.length; i++)
{
    buildSelect(blah,function(){ CallBack(xs[i],...) },...);
}

问题是当 onchange 被其中一个选择触发时,它正确地转到 CallBack() 但第一个参数不正确。例如,如果我更改第三个选择,我希望 CallBack() 用 xs[2] 调用,而不是我得到一些不同的东西,比如 xs[3] 或其他东西。

如果我稍微修改一下:

for(var i = 0; i < xs.length; i++)
{
    var xm = xs[i];
    buildSelect(blah,function(){ CallBack(xm,...) },...);
}

我仍然在 CallBack() 中得到不正确的值。有些东西告诉我这是范围/关闭相关的,但我似乎无法弄清楚是什么。

我只是希望第一个选择调用 CallBack for onchange,第一个参数为 xs[0],第二个选择为 xs[1],依此类推。我在这里做错了什么?

我应该澄清一下 xs 是一个全局变量。

谢谢

【问题讨论】:

    标签: javascript scope attachevent


    【解决方案1】:

    您需要通过在它自己的范围内关闭它来捕获 xm 值。

    为此需要单独的函数调用:

    buildCallback( curr_xm ) {
    
          // this function will refer to the `xm` member passed in
        return function(){ CallBack(curr_xm,...) },...);
    }
    
    for(var i = 0; i < xs.length; i++)
    {
        var xm = xs[ i ];
        buildSelect(blah,buildCallback( xm ),...);
    }
    

    现在回调引用的xm 是您传递给buildCallback 的那个。

    如果您需要保留 i 的其他用途,您可以改为发送:

    buildCallback( curr_i ) {
    
    
          // this function will refer to the `i` value passed in
        return function(){ CallBack( xs[ curr_i ],...) },...);
    }
    
    for(var i = 0; i < xs.length; i++)
    {
        buildSelect(blah,buildCallback( i ),...);
    }
    

    【讨论】:

    • 谢谢!确实调用第二个函数有助于解决问题。
    • @royrules22:不客气。我注意到您也可以使用现有的buildSelect 函数。只需传入ixs[i],然后在其中创建处理程序。 buildSelect(blah,xs[i],...); 这可能是最干净的。
    【解决方案2】:

    这个问题确实与作用域有关——JavaScript 只有函数作用域,没有块作用域或循环作用域。变量ixm 只有一个实例,这些变量的值随着循环的进行而变化。循环完成后,您只剩下他们持有的最后一个值。您的匿名函数捕获变量本身,而不是它们的值。

    要捕获变量的实际值,您需要另一个可以捕获局部变量的函数:

    function makeCallback(value) {
      return function() { CallBack(value, ...) };
    }
    

    makeCallback 的每次调用都会获得value 变量的一个新实例,如果你捕获了这个变量,你实际上就是捕获了这个值:

    for(var i = 0; i < xs.length; i++)
    {
        buildSelect(blah,makeCallback(xs[i]),...);
    }
    

    【讨论】:

    • 谢谢!我不知道 JS 没有块作用域。好吧,直到。再次感谢!
    【解决方案3】:

    是的,我认为关闭会有所帮助:

    for(var i = 0, l = xs.length; i < l; i++)
    {
        buildSelect(
            blah,
            function(xm){
                return function(){
                    CallBack(xm,...)
                };
            }(xs[i]),
            ...
        );
    }
    

    编辑:我还稍微优化了你的 for 循环。

    编辑:我想我会添加一个解释。您正在做的是创建一个匿名函数,该函数接受一个参数 (xm) 并立即调用该函数(后面有括号)。此匿名函数还必须将您的原始函数作为 buildSelect() 的参数返回。

    【讨论】:

    • 对不起,我不得不接受另一个问题,但在这里接受这个虚拟复选标记!感谢您的帮助!
    【解决方案4】:

    显然有一个新的 let 关键字可以满足您的要求:

    for(var i = 0; i < xs.length; i++)
    {
        let xm = xs[i];
        buildSelect(blah,function(){ CallBack(xm,...) },...);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-18
      • 2010-11-15
      • 2011-10-27
      相关资源
      最近更新 更多