简体   繁体   English

当我在for循环中复制变量时,为什么我的JavaScript代码有效

[英]Why does my JavaScript code work when I copy a variable in a for loop

This JavaScript code for a Google Chrome Extension . Google Chrome扩展程序的 JavaScript代码。

It works, but I wonder about one thing. 它有效,但我想知道一件事。 It's in the for loop that iterates an associative array and utilizes its values in a Chrome function. 它位于for循环中,迭代关联数组并在Chrome函数中使用其值。

This works just fine: 这很好用:

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 }); });
    })();
}

But with some changes it suddenly doesn't (changes are highlighted) 但随着一些变化它突然没有(突出显示变化)

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

Also I have to use the (function() { })(); 另外我必须使用(function() { })(); pattern (I don't know the name), otherwise it also doesn't work. 模式(我不知道名称),否则它也不起作用。

Question

Why does this only work when using both the pattern and the variable copying? 为什么这只在使用模式和变量复制时才有效? Please explain to me how JavaScript processes variables in a way that these contraptions are required. 请向我解释JavaScript如何以需要这些装置的方式处理变量。

There is no special scope in a for loop, so the variable is overwritten on each iteration when you do for循环中没有特殊范围,因此当您执行此操作时,每次迭代都会覆盖该变量

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

Note that the click happens later, when the for loop has completed, and because there is no new scope created inside the for loop, the variable changes on each iteration, and by the time you click the element, the href variable inside the event handler callback is the last value it was set to. 请注意,点击发生在以后,当for循环完成时,并且因为在for循环内没有创建新的作用域,变量会在每次迭代时更改,并且当您单击该元素时,事件处理程序中的href变量回调是它设置的最后一个值。

What's really happening is that any function declaration will create a new scope, so the variable isn't being overwritten on each iteration, and an Immediately Invoked Function Expression is such a function declaration, and it keeps the value of the variable constant because it creates a new scope 真正发生的是任何函数声明都会创建一个新的作用域,因此每次迭代都不会覆盖该变量,而立即调用的函数表达式就是这样一个函数声明,它保持变量的值不变,因为它创建了一个新的范围

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 }); 
        });
    })();
}

The scope of a variable declared with var is its current execution context, which is either the enclosing function or, for variables declared outside any function, global. 用var声明的变量的范围是它的当前执行上下文,它是封闭函数,或者对于在任何函数外部声明的变量,是全局的。

When you create a reference to a function, like you do when adding that event-listener, the js interpreter preserves any variable references it finds inside that function, preventing those references from being garbage-collected. 当您创建对函数的引用时,就像添加该事件侦听器时一样,js解释器会保留它在该函数内找到的任何变量引用,从而防止这些引用被垃圾回收。

In the first snippet, the preserved reference is a reference to a string, called href. 在第一个片段中,保留的引用是对字符串的引用,称为href。 Since this string is declared inside the self-executing anonymous function (the one you declare just within the for-loop), a new reference is created and preserved for each run of that function. 由于此字符串是自执行匿名函数(您在for循环中声明的函数)中声明的,因此为该函数的每次运行创建并保留一个引用。 Although each reference is named 'href', each reference is actually a unique variable, unique to that run of the anonymous function. 尽管每个引用都被命名为“href”,但每个引用实际上都是一个唯一的变量,对于该匿名函数的运行是唯一的。

Now in the second snippet, there are two references inside your event listener function: 'link' and 'links,' but those variables are declared in the outer-scope , (from the code provided, it appears to be global, but I'm betting there's some function(){ declared above, off-screen)... because this variable is declared only once, in the same scope as the for loop, the anonymous function inside the for loop is actually always referring to the same object as the other copies. 现在在第二个片段中,事件监听器函数中有两个引用:'link'和'links',但这些变量在外部范围内声明,(从提供的代码看,它看起来是全局的,但我' m bet有一些函数(){声明在屏幕外...)因为这个变量只被声明一次,在与for循环相同的范围内,for循环中的匿名函数实际上总是指向同一个对象作为其他副本。 Since the for-loop changes the value referred to by 'link' every time it loops, and every binding you created is looking at the same instance of 'link' and 'links', every function you bound will always wind up referring to the last link in the list (unless you could click so fast that you hadn't completed the for-looping when you clicked, then it would point to whichever link the for loop was currently working on.) 因为for循环每次循环都会改变'link'引用的值,并且你创建的每个绑定都在查看'link'和'links'的同一个实例,你绑定的每个函数总是会指向列表中的最后一个链接(除非您单击得太快以至于在单击时没有完成for循环,否则它将指向for循环当前正在处理的任何链接。)

(Edit: This behavior is actually pretty useful, btw - you can use it to emulate "public/private properties" seen in other languages, and to insulate properties from being changed by external code, like this: (编辑:这种行为实际上非常有用,顺便说一句 - 您可以使用它来模拟其他语言中看到的“公共/私有属性”,并使属性不被外部代码更改,如下所示:

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; 
        }
    }
};

end edit) 结束编辑)

The pattern you're referring to is called a "closure", and it's the source of endless JavaScript confusion for lots of folks. 你所指的模式被称为“闭包”,它是许多人无休止的JavaScript混淆的根源。

When you create a new function inside a function, a reference to the execution state of the current function is captured and saved for later execution of the created function. 在函数内部创建新函数时,将捕获并保存对当前函数的执行状态的引用,以便稍后执行所创建的函数。 The problem with doing so in a loop is that only one closure is created for any given containing function, so by the time you get to executing any of the created functions, the state saved in the closure is pointing to the last item in the loop. 在循环中这样做的问题是只为任何给定的包含函数创建了一个闭包,所以当你执行任何创建的函数时,闭包中保存的状态指向循环中的最后一项。

Mozilla has a great explanation of the whole concept here , including a section specifically on your issue. Mozilla 在这里对整个概念有一个很好的解释,包括一个专门讨论你的问题的部分。

为了增加adeneo的答案, 本文解释了闭包,特别是外部函数变量值范围发生变化的场景。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么这个特定的Javascript循环仅在重新分配变量时有效? - Why does this particular Javascript loop only work when reassigning a variable? 为什么我的代码只能在html中工作,而在将其放入Javascript页面时却不能工作? - Why does my code only works in html but does not work when I put it into a Javascript page? Axios - 动态标题不起作用。 为什么我的代码在动态设置变量时不起作用,但在硬编码时却起作用? - Axios - Dynamic Header not working. Why does my code not work when i set the variable dynamically but does when i hard code it? 将函数分配给“名称”变量时,为什么我的代码不起作用? - Why my code does not work when I assign a function to “name” variable? 为什么此代码可用于我的数组,但不能输入字符串数组呢? 的JavaScript - Why does this code work with my array, but not when I typed in an array of strings? JavaScript 添加媒体查询时,我的 javascript 代码不起作用? - My javascript code does not work when I add media query? 为什么删除回车后我的 javascript 不起作用? - Why does my javascript not work when I remove carriage returns? 为什么我的 JavaScript 代码在刷新时不起作用? - Why does my JavaScript code not work on refresh? 为什么我的JavaScript代码不起作用? - Why does my javascript code not work? 为什么setTimeout中的代码在JavaScript中不起作用? - Why does my code in setTimeout not work in JavaScript?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM