【问题标题】:What are the rules in JS on a variable referencing the same data across a loop? [duplicate]JS中关于跨循环引用相同数据的变量的规则是什么? [复制]
【发布时间】:2014-06-30 16:08:18
【问题描述】:

我只需要使用部分应用程序将数据传递给回调,我发现我需要一个间接层来防止循环的后续运行更改我传递给回调的数据。

这里有一个非常简单的工作示例:http://jsfiddle.net/s5gVj/

(如果 jsfiddle 不言自明,请直接跳到下面的问题)

没有间接性

这将始终将标签设置为“按下按钮 1”。

无论按下哪个按钮,都会发生这种情况。

for(var i = 0 ; i < buttons.length ; i++)
{
    var buttonID = i;
    $(buttons[i]).click(   
        function(e)
        {
            $("label").html("Button " + buttonID + " pressed.");
        });
}

间接,通过函数

同时,使用中间的函数可以解决问题

对于第一个按钮,结果将是“按下按钮 0”

对于第二个按钮,它将是“按下按钮 1”。

var buttonClickHandlerWithID = 
    function(buttonID)
    {
        return function(e)
        {
            $("label").html("Button " + buttonID + " pressed.");
        }
    }

for(var i = 0 ; i < buttons.length ; i++)
{
    $(buttons[i]).click(buttonClickHandlerWithID(i));
}


为什么会这样?函数调用是否有什么特别之处可以确保变量被复制,从而不再引用相同的数据,还是发生了其他事情?

我希望在每次迭代时重新创建一个在 for 循环中声明的变量,因此每次迭代都是独立的,但我猜不是这样?

【问题讨论】:

  • 而不是添加 buttons.length - 1 数量的 click 事件 - 您可能想要考虑委托,即将单击事件添加到父容器并按按钮类过滤目标...
  • @tborychowski 有问题的按钮是由 jQueryUI 生成的,我真的不想以一种易碎的方式摆弄它们(它们在它可以为对话框创建的按钮列表中)。所以他们共享相同的类,我可以检测到他们的位置,但我觉得这更可取。
  • @Bergi 非常正确,它会回答我的问题,谢谢。我认为,虽然这里的答案需要较少的先验知识。 (我忘记了函数/块范围,这可能会使那里的答案更难解释)。
  • @tborychowski 虽然在单独存储状态的同时可能共享点击事件?我会调查的。感谢您的提示。

标签: javascript partial-application


【解决方案1】:

JavaScript 仅具有函数作用域。函数中任何位置的所有var 声明(当然嵌套函数内部除外)都被视为出现在函数顶部。嵌套的 { ... } 块没有自己的作用域,这与 C++ 和 Java 等有很大的不同。

【讨论】:

  • 还有一个烦人的。滚动let 关键字。
  • 是的,拥有let 会很好。
  • 谢谢,这就解释了。感觉我应该知道这一点。
【解决方案2】:

就像 Pointy 回答的那样,javascript 中只有函数范围,这意味着您的代码中的 i 是在循环之外的范围内,并且稍后引用时(点击时)将是它在 for 循环之后的最终结果完成了。

如果您使用 jquery 的 .each() 而不是 for,您将获得本地范围,因为它需要一个函数作为回调。

var i = 0;
var directButtons = $("button.direct");

// jquerys each method takes a function callback
directButtons.each(function () {
    i++;
    // since this is inside a function
    var buttonID = i; // buttonID will be locally scoped

    $(this).click(function (e) {
        // this inner function "close over" (google "javascript closure") and
        // remembers the value of buttonID in the .each() loop
        $("label").html("Button " + buttonID + " pressed. " +
          "'i' will be whatever it ended up after the .each() is finished: " + i);
    });
});

http://jsfiddle.net/s5gVj/3/

还可以查看fixingthesejquery.com 的幻灯片 55


您也可以使用Immediately Invoked Function Expression 解决此问题(参见标题为“使用闭包保存状态”的部分):

var directButtons = $("button.direct");

for(var i = 0 ; i < directButtons.length ; i++){
    (function(buttonID){
        // buttonID is now scoped to this function
        $(directButtons[buttonID]).click(function (e) {
            $("label.direct").html("Button " + buttonID + " pressed. , 'i' is always " + i);
        });
    })(i); // call the function immediately, supply the value of "i" as parameter
}

http://jsfiddle.net/s5gVj/4/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-06
    • 1970-01-01
    • 2013-07-20
    • 2016-02-18
    • 2012-11-02
    相关资源
    最近更新 更多