2010-04-26 45 views
148

你曾經在引擎蓋下的JQuery的1.4源代碼採取一看,發現它是如何封裝在以下方式:JavaScript/JQuery語法如何工作:(函數(窗口,未定義){})(窗口)?

(function(window, undefined) { 

    //All the JQuery code here 
    ... 

})(window); 

我讀過JavaScript Namespacing的文章,另一個叫「An Important Pair of Parens ,「所以我知道一些關於這裏發生的事情。

但我從來沒有見過這種特殊的語法。那是什麼undefined在那裏?爲什麼window需要通過,然後再次出現在最後?

+3

我想補充說,保羅愛爾蘭在這個視頻中談到這件事:http://paulirish.com/2010/10-things-i-learned-from-the-jquery-source/ – Mottie 2011-02-09 13:51:32

+0

@Bergi你標記我的問題作爲另一個副本,但我在重複前一年問了問題。演員應該是相反的。 – dkinzer 2014-07-16 23:33:40

+0

@dkinzer:這不是關於被問及的問題,而是關於最高質量的答案。無可否認,在這種情況下,它是脖子和脖子,但我發現CMS的答案是最好的。請參閱http://chat.stackoverflow.com/rooms/17/來討論這個問題,儘管 – Bergi 2014-07-16 23:49:37

回答

154

未定義是一個正常變量,可以簡單地用undefined = "new value";來更改。所以jQuery創建了一個本地的「未定義」變量,它是真正未定義的。

由於性能原因,窗口變量是本地的。因爲當JavaScript查找一個變量時,它首先通過局部變量,直到找到變量名稱。當找不到時,JavaScript會遍歷下一個範圍等,直到它通過全局變量進行過濾。所以如果窗口變量是本地的,JavaScript可以更快地查找它。 更多信息:Speed Up Your JavaScript - Nicholas C. Zakas

+1

謝謝!這是一個非常豐富的視頻。我認爲你需要編輯你的迴應,說「窗口變量是本地的」。我確實認爲這是最好的答案。我統計並發現,窗口對象在JQuery源代碼中至少調用了17次。所以在將窗口移動到本地範圍時會產生顯着的效果。 – dkinzer 2010-04-26 20:47:26

+0

@DKinzer哦,是的,你是正確的,當然它是當地的。很高興我可以幫你=) – Vincent 2010-04-27 15:00:05

+1

根據這個更新的測試http://jsperf.com/short-scope/5傳遞'窗口'實際上比較慢,當它通過jQuery獲得一個窗口常量時,對Safari尤其不利。所以雖然'未定義'有一個用例,但我不確定'window'在所有情況下都是特別有用的。 – hexalys 2013-04-28 10:38:09

5

window像這樣傳入以防萬一有人決定在IE中重新定義窗口對象,我假設undefined相同,以防萬一它稍後以某種方式重新分配。

該腳本中的頂部window只是命名參數「window」,這是一個更局部的參數,全局參數window以及此閉包內部的代碼將使用哪個參數。最後的window實際上是指定爲第一個參數傳遞什麼,在這種情況下,當前的含義是window ......希望在這種情況發生之前,您還沒有搞砸window

這可能更容易想到通過展示jQuery中使用的最典型的案例,插件.noConflict()處理,所以對於廣大的代碼,你仍然可以使用$,即使這意味着一些其他jQuery此範圍之外:

(function($) { 
    //inside here, $ == jQuery, it was passed as the first argument 
})(jQuery); 
+0

謝謝。這很有意義。雖然,我認爲答案是這個和C.Cakas說的結合。 – dkinzer 2010-04-26 20:50:16

+0

@DKinzer恐怕我不是C. Zakas;) – Vincent 2010-04-27 15:02:01

+0

@Vincent,對不起:-) – dkinzer 2010-04-27 15:14:03

15

其他已解釋undefinedundefined就像一個全局變量,可以重新定義爲任何值。這個技巧是爲了防止所有未定義的檢查破壞,如果有人在某處寫道undefined = 10。不論變量undefined的值如何,永遠不會通過的參數保證爲實際undefined

通過窗口的原因可以用下面的例子來說明。

(function() { 
    console.log(window); 
    ... 
    ... 
    ... 
    var window = 10; 
})(); 

控制檯日誌是什麼?對象window的值是正確的?錯誤! 10?錯誤!它記錄undefined。JavaScript解釋器(或JIT編譯器)重寫這樣說 -

(function() { 
    var window; //and every other var in this function 

    console.log(window); 
    ... 
    ... 
    ... 
    window = 10; 

})(); 

但是,如果你得到window變量作爲自變量,有沒有變種,因此沒有驚喜。

我不知道jQuery是否正在這樣做,但如果您在函數的任何位置重新定義window局部變量出於任何原因,從全局範圍借用它是一個好主意。

52

未定義

通過聲明undefined作爲參數,但從未傳遞的值,以它確保它始終是未定義的,因爲它只是在全球範圍內,可以被覆蓋的變量。這使得a === undefined成爲typeof a == 'undefined'的一種安全替代方案,它可以節省幾個字符。它還使代碼更加易於縮小,因爲undefined可以縮短爲u,例如,可以節省更多字符。

窗口

傳遞window作爲參數保持在局部範圍內,這會影響性能的副本:http://jsperf.com/short-scope。所有對window的訪問現在必須在範圍鏈上少一級。與undefined一樣,本地副本再次允許更積極的縮小。


旁註:

雖然這可能沒有的jQuery的開發者的意圖,傳遞window允許更容易地集成在服務器端JavaScript環境的庫,例如node.js - 沒有全局的window對象。在這種情況下,只需要更改一行即可將另一個對象替換爲window對象。在jQuery的情況下,模擬window對象可以被創建並傳入以用於HTML抓取(諸如jsdom之類的庫可以做到這一點)。

+2

+1提到縮小,希望我能+2爲jsperf - 縮小版本爲'(function(a,b){})(window);' - ' a''和'b'比'window'和'undefined'短得多:) – gnarf 2011-02-09 13:25:43

+0

+1我以前聽說過*範圍鏈*,但忘記了這個詞。謝謝! – alex 2011-02-09 13:25:55

+0

它也是使用void(0)的一種替代方法,它的意思是出於同樣的原因:void(0)是獲取未定義值的安全方法。 – glmxndr 2011-02-09 13:26:23

5

經過1000000次迭代測試。這種本地化對性能沒有影響。在1000000次迭代中甚至沒有一毫秒。這簡直是​​無用的。

+2

更不用說,閉包帶有函數實例的所有變量,所以如果你有100關閉中的字節數和1000個具有更多內存的100kb的實例。 – 2013-07-31 08:50:35

+0

@AkashKava和@Semra,我不認爲這個測試真正解釋了爲什麼你會在現實世界的情況下通過窗口。性能增益只有在深度嵌套閉包可以在範圍鏈中進一步訪問該變量時才能看到。顯然,如果你的函數只是從變量的作用域鏈上離開一步,你就不會看到太多的收穫。但如果它在那裏等待並需要獲得'''window''',你可能會看到本地副本的一些收益。 – gabereal 2014-12-02 21:42:59

+0

@ gabereal新的JavaScript引擎在編譯時解決了閉包問題,運行時在閉包時沒有性能增益。我懷疑是否有任何可衡量的性能增益,如果您非常自信,可以創建jsperf示例來演示性能。我們唯一的表現是通過隔離閉合來實現更好的縮小效果,從而縮小尺寸,並通過更快地下載和解析腳本來實現性能。 – 2014-12-04 10:59:49