2011-03-06 103 views
28

在回顧source code for CoffeeScript on Github,我注意到大部分,如果不是全部的模塊定義如下:模式的CoffeeScript的模塊

(function() { 
    ... 
}).call(this); 

這種模式看起來像它包裝的整個模塊中,一個匿名函數和自稱。

這種方法的優點(和缺點)是什麼?還有其他方法可以實現相同的目標嗎?

+5

請注意,您正在查看生成的JavaScript,而不是寫入的咖啡文本。 – Dykam 2011-03-06 16:28:57

回答

77

Harmen的回答非常好,但讓我詳細介紹一下CoffeeScript編譯器和原因所做的工作。

當您編譯coffee -c foo.coffee的東西,你總是會得到foo.js,看起來像這樣:

(function() { 
    ... 
}).call(this); 

這是爲什麼?那麼,假設你把一個任務如

x = 'stringy string' 

foo.coffee。當它看到的時候,編譯器會問:x已經存在於這個範圍內,還是外部範圍?如果不是,則在JavaScript輸出中將該範圍的頂部放置一個var x聲明。

現在假設你寫

x = 42 

bar.coffee,編譯兩者並用bar.js部署串聯foo.js。你會得到

(function() { 
    var x; 
    x = 'stringy string'; 
    ... 
}).call(this); 
(function() { 
    var x; 
    x = 42; 
    ... 
}).call(this); 

所以在foo.coffeexbar.coffeex彼此完全隔離。這是CoffeeScript的一個重要部分:變量不會從一個.coffee文件泄露到另一個文件,除非明確導出(通過連接到共享全局或在Node.js中連接到exports)。

您可以通過使用-b(「裸」)標誌覆蓋coffee,但這應該只用於非常特殊的情況。如果你使用上面的例子,你會得到的輸出將是

var x; 
x = 'stringy string'; 
... 
var x; 
x = 42; 
... 

這可能會有可怕的後果。要親自測試,請嘗試在foo.coffee中添加setTimeout (-> alert x), 1。並且請注意,您不必自己連接兩個JS文件 - 如果您使用兩個獨立的<script>標記將它們包含在頁面中,它們仍然可以有效地作爲一個文件運行。

通過隔離不同模塊的作用域,CoffeeScript編譯器讓您不必擔心項目中的不同文件是否可能使用相同的本地變量名稱。這是JavaScript世界中的常見做法(例如,請參見jQuery source,或者幾乎任何jQuery插件)-CoffeeScript都會爲您提供幫助。

+1

優秀的戰略! – lo5 2011-03-07 07:26:56

+4

您的答案寫得很好,真的可以幫助我更好地理解CoffeeScript(和JavaScript)......謝謝! – 2011-03-09 13:15:45

+1

heck玩這樣的全局變量的人是誰?沒有課堂或封閉,我不會想到做任何事情。不得不明確出口是令人厭惡的。 – Duke 2011-09-22 08:58:00

19

這種方法的好處是,它創建私有變量,所以不會有變量名的任何衝突:

(function() { 
    var privateVar = 'test'; 
    alert(privateVar); // test 
})(); 

alert(typeof privateVar); // undefined 

添加的.call(this)使得this關鍵字指的是相同的值它指的是功能之外。如果未添加,this關鍵字將自動引用全局對象。

一個小例子以示區別如下:

function coffee(){ 
    this.val = 'test'; 
    this.module = (function(){ 
    return this.val; 
    }).call(this); 
} 

var instance = new coffee(); 
alert(instance.module); // test 

function coffee(){ 
    this.val = 'test'; 
    this.module = (function(){ 
    return this.val; 
    })(); 
} 

var instance = new coffee(); 
alert(typeof instance.module); // undefined 
+0

很好的答案。謝謝你的幫助! – 2011-03-09 13:14:06

1

這類似於語法這樣:

(function() { 

}()); 

其被稱爲即時功能。該功能被定義並立即執行。這個優點是你可以將所有的代碼放在這個塊中,並且將這個函數分配給一個單獨的全局變量,從而減少全局命名空間的污染。它在函數中提供了一個很好的包含範圍。

這是典型的圖案寫入模塊時,我使用:

var MY_MODULE = (function() { 
    //local variables 
    var variable1, 
     variable2, 
     _self = {}, 
     etc 

    // public API 
    _self = { 
     someMethod: function() { 

     } 
    } 

    return _self; 
}()); 

不知道利弊可能是究竟是什麼,如果別人知道的任何我會很樂意瞭解他們。

+0

我想說的是:1)你必須努力工作,以便在模塊之間共享狀態(至少在CoffeeScript中 - 在JavaScript中,你可以省略'var',但通常不鼓勵,例如通過jsLint),以及2)額外的函數調用和字節有一小部分開銷。但99%的時間,額外的安全是值得的。 – 2011-03-06 18:29:16

+0

不,這不是一個相關的答案。他正在問(函數(){})。具體調用(this),而不是(function(){})()。哈門的回答是關鍵的。 – 2011-10-12 10:38:53