深刻JavaScript模塊化編程

今天看requirejs官網的manual,發現了下面這篇好文章,因而花點時間翻譯了一下,翻譯很差的地方請指正,謝謝!
 
 
模塊式編程市一中廣泛的JavaScript編程模式。它很好理解,可是有一些高級用法還沒被不少人注意到。在這篇文章中,我會回顧一些基本用戶,同時包含一些很是有用的高級應用,也包括一個最原始應用。
      
基本應用
 
咱們簡單過一遍模塊化編程。該模式從三年前, YUI的Eric  Miraglia在他的博客中第一次提起它,就被開始普遍的認識。 若是已經很熟悉該模式,能夠直接跳到「高級應用」部分。
 
匿名閉包
 
這是有一個最基本的構造函數組成,它真的是JavaScript最好的功能。咱們只是簡單的建立一個匿名函數,而後立刻執行它。全部在函數裏面的代碼將運行在閉包內,這爲咱們應用程序的生命週期提供私有性和狀態。


匿名函數由一個()括號包起來。這是由於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   

相關文章
相關標籤/搜索