2015-05-01 29 views
2

此代碼爲谷歌瀏覽器擴展程序爲什麼我的JavaScript代碼在for循環中複製變量時工作

它的工作原理,但我想知道一件事。它在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是如何處理變量的,這些變量是需要這些工具的。

回答

4

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

+0

有趣。學到了更多關於JavaScript的知識。感謝您的好回答:) – bytecode77

1

您所指的模式稱爲「閉包」,它是許多人無盡的JavaScript混淆的源頭。

在函數中創建新函數時,會捕獲並保存對當前函數的執行狀態的引用,以便稍後執行創建的函數。在一個循環中這樣做的問題是,對於任何給定的包含函數,只有一個閉包被創建,所以當你開始執行任何創建的函數時,保存在閉包中的狀態指向循環中的最後一個項目。

Mozilla對整個概念here有很好的解釋,其中包括一個專門針對您問題的部分。

2

當你創建一個參考的作用,就像你補充說,事件偵聽器時做的JS解釋保留任何變量引用它發現裏面的功能,防止被當作垃圾回收的引用。

在第一片段中,所保存的參考是一個字符串,稱爲HREF的參考。由於這個字符串聲明自執行的匿名函數(聲明只是for循環中的一個),創建一個新的參考和保存該功能的每次運行。雖然每個引用都被命名爲'href',但每個引用實際上都是唯一的變量,對於匿名函數的運行是唯一的。

現在,在第二個片段,也有你的事件偵聽器函數內引用:「鏈接」和「鏈接」,但這些變量在外範圍聲明,(從提供的代碼,它似乎是全球性的,但我打賭有一些函數()(上面聲明,離屏)...因爲這個變量只聲明一次,在for循環相同的範圍內,for循環內的匿名函數實際上始終是指相同的對象作爲其他副本。由於for循環變化的「鏈接」每次循環時間,結合創建所指的價值,是看「鏈接」和「鏈接」的同一個實例,每次界將永遠拉閘功能指到列表中的最後一個鏈接(除非您點擊的速度如此之快以至於單擊時沒有完成for-looping,那麼它將指向for循環當前正在處理的鏈接)。

(Edit :這種行爲實際上是非常有用的,順便說一句 - 你可以用它來模擬在別的語言中看到「公共/私人性質」,並從外部代碼被改變絕緣性能,如:

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

結束編輯)

1

要添加到adeneo的答案,此article解釋封閉,特別是外部函數的變量值的範圍更改的情況。

相關問題