簡體   English   中英

當我在for循環中復制變量時,為什么我的JavaScript代碼有效

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

Google Chrome擴展程序的 JavaScript代碼。

它有效,但我想知道一件事。 它位於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];                                            Look  -----v
[...].addEventListener('click', function() { chrome.tabs.create({ url: links[link] }); });

另外我必須使用(function() { })(); 模式(我不知道名稱),否則它也不起作用。

為什么這只在使用模式和變量復制時才有效? 請向我解釋JavaScript如何以需要這些裝置的方式處理變量。

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聲明的變量的范圍是它的當前執行上下文,它是封閉函數,或者對於在任何函數外部聲明的變量,是全局的。

當您創建對函數的引用時,就像添加該事件偵聽器時一樣,js解釋器會保留它在該函數內找到的任何變量引用,從而防止這些引用被垃圾回收。

在第一個片段中,保留的引用是對字符串的引用,稱為href。 由於此字符串是自執行匿名函數(您在for循環中聲明的函數)中聲明的,因此為該函數的每次運行創建並保留一個引用。 盡管每個引用都被命名為“href”,但每個引用實際上都是一個唯一的變量,對於該匿名函數的運行是唯一的。

現在在第二個片段中,事件監聽器函數中有兩個引用:'link'和'links',但這些變量在外部范圍內聲明,(從提供的代碼看,它看起來是全局的,但我' m bet有一些函數(){聲明在屏幕外...)因為這個變量只被聲明一次,在與for循環相同的范圍內,for循環中的匿名函數實際上總是指向同一個對象作為其他副本。 因為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; 
        }
    }
};

結束編輯)

你所指的模式被稱為“閉包”,它是許多人無休止的JavaScript混淆的根源。

在函數內部創建新函數時,將捕獲並保存對當前函數的執行狀態的引用,以便稍后執行所創建的函數。 在循環中這樣做的問題是只為任何給定的包含函數創建了一個閉包,所以當你執行任何創建的函數時,閉包中保存的狀態指向循環中的最后一項。

Mozilla 在這里對整個概念有一個很好的解釋,包括一個專門討論你的問題的部分。

為了增加adeneo的答案, 本文解釋了閉包,特別是外部函數變量值范圍發生變化的場景。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM