匿名函數由一個()括號包起來。這是由於JavaScript語言認爲以全局引入
JavaScript有一個叫隱含全局變量的特徵。任什麼時候候,一個變量名被使用,編譯器會往回遍歷做用域鏈,知道找到這個變量名的var聲明語句。若是沒找到,則該變量當成全局變量,若是是爲它賦值,則會爲它建立一個全局變量。這意味着,在匿名函數中,很是容易建立或使用全局變量。不幸的是,這會致使代碼很是難管理,由於對於程序員來講,不知道代碼中哪一個變量是全局變量。
幸運的是,匿名函數提供了一個簡單的解決辦法,經過傳入一個全局的參數到匿名方法來引用它們,這樣一來就比引用全局變量更加清晰和快速。下面是這個例子:
(function () { // ... all vars and functions are in this scope only // still maintains access to all globals }());function爲開頭的語句,都是定義函數的語句。添加括號就變成了建立函數表達式。
(function ($, YAHOO) { // now have access to globals jQuery (as $) and YAHOO in this code }(jQuery, YAHOO));
模塊
有時候咱們不僅須要使用全局變量,還須要聲明他們。咱們能夠經過大家函數的return value,很容易來公開這些全局變量。這樣咱們就完成一個基本的模塊化設計,下面是完整的代碼示例:
公開
注意到咱們這裏定義了一個MODULE的全局變量,包含了兩個公開屬性:一個名爲MODULE.moduleMethod方法和var MODULE = (function () { var my = {}, privateVariable = 1; function privateMethod() { // ... } my.moduleProperty = 1; my.moduleMethod = function () { // ... }; return my; }());
MODULE.moduleProperty屬性。另外,經過匿名閉包,還能夠維護着一個私有的內部狀態。同時,咱們還可使用上面的方法,輕易的引入全局變量。
高級應用
雖然上面的模式可以應付許多使用場景,不過咱們能夠更深刻的瞭解該默認,來穿建更多強大的,具備擴張性的構件。還需繼續上面的模塊MODULE,讓咱們一個個使用。
擴展
目前該模式的一個限制就是,整個模塊都必須在一個文件中。任何曾經在大型代碼量下工做的人,都知道把代碼分開成幾個文件的價值。幸運的是,咱們有一個很好的辦法來擴展模塊。首先,咱們引入模塊,而後繼續添加屬性,再把它暴露出去。下面是例子,傳入的參數是上面的MODULE
var MODULE = (function (my) { my.anotherMethod = function () { // added method... }; return my; }(MODULE));
爲了保持代碼一致,咱們在MODULE前加上var,雖然能夠去掉。上面代碼運行後,咱們的模塊會多出一個公開函數:MODULE.anotherMethod,這個擴展文件同事會維護者它本身私有的內部狀態和引入的內容。
鬆散擴展
雖然咱們上面例子要求先初始化模塊,而後在擴展模塊新的內容,可是並非非得這樣。JavaScript應用程序最後的一個地方,就是能夠異步加載腳本文件。咱們能夠經過鬆散擴展,建立靈活的,能夠已任何順序加載的,多文件模塊。每一個文件須要按照下面的代碼結構:
在這個模式下,var關鍵字都必須寫上。由於這裏的會建立模塊,若是該模塊還未存在。這意味着你可使用想LABjs這樣的工具,異步加載模塊文件,而不用阻塞進程。
var MODULE = (function (my) { // add capabilities... return my; }(MODULE || {}));
緊耦合擴展
雖然鬆散擴展挺好的,可是也存在一些限制。最重要的是,你不能安全的重寫模塊的屬性。同時,在初始化的過程當中,你不能使用模塊的其它屬性(可是你能夠在初始化完成後的運行時使用)。緊耦合擴展須要按必定順序加載,可是它支持重載。下面是一個簡單例子(參數仍是以前的MODULE):
var MODULE = (function (my) { var old_moduleMethod = my.moduleMethod; my.moduleMethod = function () { // method override, has access to old through old_moduleMethod... }; return my; }(MODULE));
這裏咱們重寫了MODULE.moduleMethod方法,同時,保留着舊函數的一個引用,以便將來須要。
克隆與繼承
這個模式多是最缺少靈活性的一個了。它運行一些整齊的組合,可是確犧牲了靈活性。正如上面的代碼,對象或者函數的屬性不會重複,他們存在同個對象中的兩個引用。修改其中一個,同時也會改動另一個。var MODULE_TWO = (function (old) { var my = {}, key; for (key in old) { if (old.hasOwnProperty(key)) { my[key] = old[key]; } } var super_moduleMethod = old.moduleMethod; my.moduleMethod = function () { // override method on the clone, access to super through super_moduleMethod }; return my; }(MODULE));
跨文件私有狀態html
才分模塊成多個文件的一個最主要的限制是,它們每一個文件都維護者本身的私有狀態,並且不能訪問其它文件的私有狀態。這是能夠修復的。
下面就是一個使用鬆散擴展模塊,同時能夠維護全部擴展的私有狀態的例子:程序員
var MODULE = (function (my) { var _private = my._private = my._private || {}, _seal = my._seal = my._seal || function () { delete my._private; delete my._seal; delete my._unseal; }, _unseal = my._unseal = my._unseal || function () { my._private = _private; my._seal = _seal; my._unseal = _unseal; }; // permanent access to _private, _seal, and _unseal return my; }(MODULE || {}));
任何文件均可以設置本地屬性_private, 並且它立刻就能夠從其它文件反問到。一旦這個模塊加載完成,應用程序必須調用MODULE._seal(),組織外部環境修改內部的_privaet變量。若是module有添加新的擴展,在程序的生命週期內,人和一個內部函數,任何文件中,在加載文件以前調用_unseal()方法,而後執行完成後再調用_seal()。這個方法是今天上班的時候想出來的,我還沒在別的地方看過。我想它是一個頗有用的模式,所以值得單獨來寫這一塊內容。ajax
子模塊編程
咱們最後一個高級應用實際上是最簡單的。在不少狀況下建立子模塊是很是有用的。它就跟建立一個普通的模塊同樣數組
MODULE.sub = (function () {
var my = {};
// ...安全
return my;
}());閉包
雖然這很簡單,可是仍是值得把它包含進來。子模塊因爲普通模塊的有點,包含擴展功能和私有狀態。異步
總結ide
大多數的高級應用均可以和其它應用結合一塊兒,從而建立更好的模式。若是必定要我指出一個設計複雜應用程序的組合,
我會合並鬆散模式,私有狀態和子模塊。模塊化
在這裏我沒有涉及到性能問題,可是我想在這裏說:這些模塊化模式性能都很好。他們能很好的壓縮,讓加快下載代碼的時間。
使用鬆散擴展運行非阻塞並行下載文件,同時也提升下載的速度。初始化時間可能慢於其它方法,可是值得的。運行時應該也不會
有什麼問題,由於全局變量被正確的包含進去。並且子模塊由於縮短了本地變量的引用鏈,反而能提升一些速度。
最後,下面是一個子模塊的例子,針對於它的父模塊(若是不存在則建立),它本身能夠動態加載自己。
這裏沒有包含私有狀態,可是包含進來是很簡單的。這個模式運行整個複雜的代碼結構異步的加載自己以及其子模塊。
var UTIL = (function (parent, $) {
var my = parent.ajax = parent.ajax || {};
my.get = function (url, params, callback) {
// ok, so I'm cheating a bit :)
return $.getJSON(url, params, callback);
};
// etc...
return parent;
}(UTIL || {}, jQuery));
我但願本文對你有幫助,請留言大家的想法。如今,更好去的編寫模塊化程序吧!
Reference: http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html