【问题标题】:Why does my JavaScript code work when I copy a variable in a for loop当我在 for 循环中复制变量时,为什么我的 JavaScript 代码可以工作
【发布时间】:2015-07-11 19:25:18
【问题描述】:

此 JavaScript 代码用于 Google Chrome 扩展程序

它有效,但我想知道一件事。它在 for 循环中迭代关联数组并在 Chrome 函数中利用其值。

这很好用:

var links =
{
    apps: 'chrome://apps/',
    bookmarks: 'chrome://bookmarks/',
    history: 'chrome://history',
    ...
};

for (var link in links)
{
    (function()
    {
        var href = links[link];
        document.querySelector('#' + link).addEventListener('click', function() { chrome.tabs.create({ url: href }); });
    })();
}

但是随着一些变化它突然不(变化被突出显示)

var href = links[link]; 看-----v
[...].addEventListener('click', function() { chrome.tabs.create({ url: links[link] }); });

我也必须使用(function() { })();模式(我不知道名字),否则它也不起作用。

问题

为什么这仅在同时使用模式和变量复制时才有效?请向我解释 JavaScript 如何以需要这些装置的方式处理变量。

【问题讨论】:

    标签: javascript variables for-loop


    【解决方案1】:

    for 循环中没有特殊作用域,因此每次迭代都会覆盖变量

    for (var link in links) {
        var href = links[link];
        element.addEventListener('click', function() { 
            chrome.tabs.create({ url: href }); 
        });
    }
    

    请注意,单击发生在稍后,当 for 循环完成时,并且由于在 for 循环内没有创建新范围,因此每次迭代时变量都会更改,并且当您单击元素时,href事件处理程序回调中的变量是它设置的最后一个值。

    真正发生的是任何函数声明都会创建一个新范围,因此变量不会在每次迭代时被覆盖,而立即调用函数表达式就是这样的函数声明,它会保持变量的值不变因为它创建了一个新范围

    for (var link in links) {
        (function() { // new scope, variables inside this isn't changed on next iteration
            var href = links[link];
            element.addEventListener('click', function() { 
                chrome.tabs.create({ url: href }); 
            });
        })();
    }
    

    用 var 声明的变量的作用域是它当前的执行 上下文,它可以是封闭函数,也可以是变量 在任何函数之外声明,全局。

    【讨论】:

    • 有趣。学习了更多关于 JavaScript 的知识。谢谢你的好答案:)
    【解决方案2】:

    当您创建对函数的引用时,就像添加事件侦听器时所做的那样,js 解释器会保留它在该函数中找到的所有变量引用,从而防止这些引用被垃圾收集。

    在第一个 sn-p 中,保留的引用是对字符串的引用,称为 href。由于这个字符串被声明为 inside 自执行匿名函数(你在 for 循环中声明的那个),一个 new 引用为每次运行创建并保留那个功能。尽管每个引用都被命名为“href”,但每个引用实际上都是一个唯一变量,对于匿名函数的运行来说是唯一的。

    现在在第二个 sn-p 中,您的事件侦听器函数中有 两个 引用:'link' 和 'links',但这些变量是在 outer-scope,(从提供的代码来看,它似乎是全局的,但我敢打赌上面声明了一些 function(){,屏幕外)......因为这个变量只声明了一次,在相同的范围内在 for 循环中,for 循环中的匿名函数实际上总是引用 same 对象作为其他副本。由于 for 循环每次循环都会更改 'link' 引用的值,并且您创建的 每个 绑定都在查看 'link' 和 'links' 的同一个实例,因此您绑定的每个函数将始终指向列表中的最后一个链接(除非您单击得如此之快以至于单击时尚未完成 for 循环,否则它将指向 for 循环当前正在处理的任何链接。)

    (编辑:顺便说一句,这种行为实际上非常有用 - 您可以使用它来模拟在其他语言中看到的“公共/私有属性”,并防止属性被外部代码更改,如下所示:

    var MyCounter = Counter();
    
    MyCounter.Increment();
    MyCounter.Increment();
    MyCounter.Increment();
    console.log(MyCounter.GetCount());
    
    function Counter() {
        // count is a locally-scoped variable, inaccessible to the outside world
        var count = 0;
    
        // The object below is what's going to be assigned to MyCounter
        //  the functions hold references to count, so it won't be garbage-collected
        //  count will also be unique to each run of Counter()
        return { 
            Increment : function() { 
                count++; 
            }, 
            GetCount : function() { 
                return count; 
            }
        }
    };
    

    结束编辑)

    【讨论】:

      【解决方案3】:

      您所指的模式称为“闭包”,它是许多人对 JavaScript 无休止的困惑的根源。

      当您在函数内部创建新函数时,会捕获并保存对当前函数执行状态的引用,以供以后执行创建的函数。在循环中这样做的问题是,对于任何给定的包含函数,只会创建一个闭包,所以当你开始执行任何创建的函数时,保存在闭包中的状态指向循环中的最后一项.

      Mozilla 对here 的整个概念有很好的解释,包括专门针对您的问题的部分。

      【讨论】:

        【解决方案4】:

        为了补充 adeneo 的答案,这个 article 解释了闭包,尤其是外部函数的变量值范围发生变化的场景。

        【讨论】:

          猜你喜欢
          • 2019-04-12
          • 1970-01-01
          • 2019-03-07
          • 1970-01-01
          • 1970-01-01
          • 2019-04-09
          • 1970-01-01
          • 2022-11-07
          • 2022-08-04
          相关资源
          最近更新 更多