小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

JavaScript模塊化開發(fā)一瞥

 旭龍 2012-06-30

 對(duì)于那些初學(xué)JavaScript、同時(shí)又正用它構(gòu)建大型應(yīng)用程序的開發(fā)者而言,必須面對(duì)的首要挑戰(zhàn)是,該如何組織代碼。盡管起初通過(guò)在<script>標(biāo)記之間嵌入數(shù)百行代碼就能跑起來(lái),不過(guò)很快代碼會(huì)變得一塌糊涂。其中的難點(diǎn)在于,對(duì)于組織我們的代碼,JavaScript并未提供任何明顯幫助。從字面上看,C#有using,Java有import——而JavaScript一無(wú)所有。這就迫使JavaScript作者去嘗試各種不同約定(conventions),并用我們擁有的這種語(yǔ)言創(chuàng)建了一些實(shí)踐方法來(lái)組織大型JavaScript應(yīng)用程序。

形成現(xiàn)代JavaScript基礎(chǔ)的那些模式、工具及實(shí)踐必將來(lái)自語(yǔ)言本身以外的實(shí)現(xiàn)

—— Rebecca Murphy

  模塊模式(The Module Pattern)

  解決此問(wèn)題使用最為廣泛的方法是模塊模式(Module Pattern)。我嘗試在下面解釋一個(gè)基本示例,并談?wù)撍囊恍傩?。?duì)于各種不同方法更好的描述和夢(mèng)幻般的運(yùn)行,請(qǐng)參閱Ben Cherry的帖子——JavaScript Module Pattern: In-Depth(深入理解JavaScript模塊模式)。

(function(lab49) {
function privateAdder(n1, n2) {
return n1 + n2;
}
lab49.add = function(n1, n2) {
return privateAdder(n1, n2); // 原文代碼有誤,已修正。
};
})(window.lab49 = window.lab49 || {});

  上面的示例中,我們只使用一些來(lái)自語(yǔ)言本身的基本功能,就創(chuàng)建了曾在C#和Java等語(yǔ)言中見過(guò)的類似結(jié)構(gòu)。

  隔離(Isolation)

  你會(huì)注意到這段代碼包在一個(gè)立即調(diào)用的函數(shù)里(查看最后一行)。在瀏覽器中,默認(rèn)情況下會(huì)在全局范圍(global scope)級(jí)別上對(duì)JavaScript文件進(jìn)行評(píng)估(evaluated),因此在我們?cè)谖募?nèi)聲明的任何內(nèi)容都是隨處可用的。想象一下,如果在lib1.js中有句var name = '...',而lib2.js中有另一句var name = '...'。那么第二個(gè)var語(yǔ)句會(huì)替掉第一句的值——這可不太妙。然而,由于JavaScript擁有函數(shù)作用域(function scoping)級(jí)別,上例中所聲明的一切都在該函數(shù)自身作用域中,與全局作用域相脫離。這意味著,無(wú)論未來(lái)在系統(tǒng)中發(fā)生什么,位于該函數(shù)中的任何內(nèi)容都會(huì)被隔離開來(lái)。

  命名空間(Namespacing)

  在最后一行中,你會(huì)發(fā)現(xiàn)我們要么把window.lab49賦給其自身,要么把空對(duì)象直接量(empty object literal)賦給它。盡管看起來(lái)有些奇怪,但是讓我們一起看下某個(gè)虛構(gòu)的系統(tǒng),在那里我們擁有若干js文件,所有文件都用了上例中的函數(shù)包裝器(function wrapper)。

  首個(gè)包含進(jìn)來(lái)的文件會(huì)評(píng)估OR(邏輯或)語(yǔ)句,并發(fā)現(xiàn)左側(cè)表達(dá)式為undefined(未定義)。由于undefined是虛假值(falsely value),因此OR語(yǔ)句會(huì)繼續(xù)評(píng)估右側(cè)表達(dá)式,本例中是個(gè)空對(duì)象直接量。此OR語(yǔ)句實(shí)際上是個(gè)會(huì)返回評(píng)估結(jié)果的表達(dá)式,然后將結(jié)果賦給全局變量window.lab49。

  現(xiàn)在,輪到下個(gè)文件來(lái)使用此模式了,它會(huì)獲得OR語(yǔ)句,并發(fā)現(xiàn)window.lab49當(dāng)前是對(duì)象實(shí)例 — — 真值(truthy value)。OR語(yǔ)句會(huì)短路并返回這個(gè)值,并將此值立即賦給其自身 — — 實(shí)際上啥也沒(méi)做。

  這導(dǎo)致的結(jié)果是,首個(gè)包含進(jìn)來(lái)的文件會(huì)創(chuàng)建我們的lab49命名空間(只是個(gè)JavaScript對(duì)象),而且每個(gè)使用這種結(jié)構(gòu)的后續(xù)文件都只不過(guò)是重用這個(gè)現(xiàn)有實(shí)例。

  私有狀態(tài)(Private State)

  正如我們剛才所說(shuō),由于位于函數(shù)內(nèi)部,在其內(nèi)部聲明的一切內(nèi)容都是處于該函數(shù)的范圍內(nèi),而不是全局范圍。對(duì)于隔離我們的代碼這真太棒了,此外,它還有個(gè)影響是,沒(méi)有人能調(diào)用它。中看不中用。

  剛剛我們還談到,我們創(chuàng)建了window.lab49對(duì)象來(lái)有效管地理我們內(nèi)容的命名空間。而且這個(gè)lab49變量是全局可用的,因?yàn)樗桓郊拥?code style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">window對(duì)象上。要想把我們模塊中的內(nèi)容暴露給外部,你可以公開地說(shuō),我們要做的就是把一些值附加到全局變量上。正如我們?cè)谏侠袑?duì)add函數(shù)所做的一樣?,F(xiàn)在,在我們的模塊外部可以通過(guò)lab49.add(2, 2)來(lái)調(diào)用我們的add函數(shù)了。

  在此函數(shù)內(nèi)聲明我們的值的另一結(jié)果是,如果某個(gè)值不是通過(guò)將其附加到我們的全局命名空間或者模塊外部的某物的方法來(lái)顯示公開的,那么外部代碼將無(wú)法碰到它。事實(shí)上,我們剛剛就創(chuàng)建了一些私有值。

  CommonJS模塊(CommonJS Modules)

  CommonJS是一個(gè)主要由服務(wù)端JavaScript運(yùn)行庫(kù)(server-side JavaScript runtimes)作者組成的小組,他們一直致力于暴露及訪問(wèn)模塊的標(biāo)準(zhǔn)化工作(standardize exposing and accessing modules)。值得注意的是,盡管他們提議的模塊系統(tǒng)不是來(lái)自于創(chuàng)建JavaScript標(biāo)準(zhǔn)同一小組的一個(gè)標(biāo)準(zhǔn),因此它更多地成為JavaScript運(yùn)行庫(kù)作者之間的非正式約定(informal convention)。

我通常支持CommonJS的想法,但要搞清楚的是:它并不是一份崇高而神圣的規(guī)范(就像ES5一樣);它不過(guò)是某些人在郵件列表中所討論的想法。而且這些想法多數(shù)都沒(méi)有付諸實(shí)現(xiàn)。

—— Ryan Dahl, node.js的創(chuàng)造者

  該模塊規(guī)范(Modules specification)的核心可謂開門見山。模塊(Modules)在它們自己的上下文中進(jìn)行評(píng)估,并且擁有全局變量exports以供模塊使用。變量exports只是個(gè)普通的JavaScript對(duì)象(plain old JavaScript object),甚至你也可以往它上面附加內(nèi)容,與我們上面展示的命名空間對(duì)象類似。為了訪問(wèn)某個(gè)模塊,你要調(diào)用全局函數(shù)require,并指明你請(qǐng)求的包的標(biāo)示符(identifier for the package)。然后評(píng)估該模塊,并且無(wú)論返回什么都會(huì)附加到exports上。此模塊將會(huì)緩存起來(lái),以便后來(lái)的require函數(shù)調(diào)用來(lái)使用。

// calculator.js
exports.add = function(n1, n2) {
};
// app.js
var calculator = require('./calculator');
calculator.add(2, 2);

  如果你曾經(jīng)玩過(guò)Node.js,那么你會(huì)發(fā)現(xiàn)上面的代碼很熟悉。這種用Node來(lái)實(shí)現(xiàn)CommonJS模塊的方式是出奇地簡(jiǎn)單,在node-inspector(一款Node調(diào)試器)中查看某個(gè)模塊時(shí)將顯示其包裝在某個(gè)函數(shù)內(nèi)部的內(nèi)容,此函數(shù)正是傳遞給exportsrequire的值。非常類似于我們上面展示的手?jǐn)€模塊。

  有幾個(gè)node項(xiàng)目(Stitch和Browserify),它們將CommonJS模塊帶進(jìn)了瀏覽器。服務(wù)器端組件將這些彼此獨(dú)立的模塊js文件塞進(jìn)一個(gè)單獨(dú)的js文件中,并在那些代碼外面包上生成的模塊包裝器(generated module wrapper)。

  CommonJS主要設(shè)計(jì)用于服務(wù)端JavaScript運(yùn)行庫(kù),而且由于有幾個(gè)屬性使得它們很難在瀏覽器中進(jìn)行客戶端代碼的組織。

  • require必須立即返回——當(dāng)你已經(jīng)擁有所有內(nèi)容時(shí)這會(huì)工作得非常好,但是當(dāng)使用腳本加載器(script loader)異步下載腳本時(shí)就會(huì)有困難。
  • 每個(gè)文件一個(gè)模塊——為了合并為CommonJS模塊,必須把它們包裹到一個(gè)函數(shù)中,然后再組織為某種式樣。如果沒(méi)有某些服務(wù)器組件,正如上面提到的那些,就會(huì)讓它們難以使用,并且在許多環(huán)境(ASP.NET,Java)下這些服務(wù)器組件尚不存在。

  異步模塊定義(Asynchronous Module Definition)

  異步模塊定義(Asynchronous Module Definition,通常稱為AMD)已設(shè)計(jì)為適合于瀏覽器的模塊格式。它最初只是一個(gè)來(lái)自CommonJS小組的提議,但此后移到了GitHub上,而且現(xiàn)在伴有一個(gè)適用于模塊系統(tǒng)作者的測(cè)試套件,以便驗(yàn)證對(duì)于AMD API的遵從性(compliance)。

  AMD的核心是define函數(shù)。調(diào)用define函數(shù)最常見的方式是接受三個(gè)參數(shù)——模塊名(也就是說(shuō)不再與文件名綁定)、該模塊依賴的模塊標(biāo)識(shí)符數(shù)組、以及工廠函數(shù),它將返回該模塊的定義。(還有其他的方式調(diào)用define函數(shù)——詳細(xì)信息參閱AMD wiki)。

define('calculator', ['adder'], function(adder) {
return {
add: function(n1, n2) {
return adder.add(n1, n2);
}
};
});

  由于此模塊的定義包在define函數(shù)的調(diào)用中,因此這就意味著,你可以愉快地在單個(gè)js文件內(nèi)擁有多個(gè)模塊。此外,由于當(dāng)調(diào)用define模塊工廠函數(shù)時(shí),模塊加載器擁有控制權(quán),因此它可在閑暇之余解決(模塊之間的)依賴關(guān)系——如果那些模塊必須首先異步下載,那就會(huì)很方便了。

  為了與原本的CommonJS模塊提議保持兼容已作出重大努力。當(dāng)在模塊工廠函數(shù)中使用requireexports時(shí)會(huì)有特殊處理,這意味著,那些傳統(tǒng)的CommonJS模塊可直接拿來(lái)用。

  看起來(lái)AMD正在成為頗受歡迎的組織客戶端JavaScript應(yīng)用程序的方式。無(wú)論是否通過(guò)如RequireJS或curl.js、或是像Dojo等最近已采用AMD的JavaScript應(yīng)用程序等模塊資源加載器來(lái)組織代碼。

  這是否意味著JavaScript很爛?(Does this mean JavaScript sucks?)

  缺乏將代碼組織到模塊中的語(yǔ)言級(jí)別的結(jié)構(gòu)(language level constructs),這可能會(huì)讓來(lái)自于其他語(yǔ)言的開發(fā)者感覺(jué)很不爽。然而,正由于此缺陷才迫使JavaScript開發(fā)者想出他們自己的模塊構(gòu)造模式,我們已經(jīng)能夠隨著JavaScript應(yīng)用程序的發(fā)展進(jìn)行迭代和改進(jìn)。欲深入了解此主題請(qǐng)?jiān)L問(wèn)Tagneto的博客。

  想象一下,即使這種功能類型(即Module)在10年前就已包括在語(yǔ)言中。那么他們也不可能想到在服務(wù)器上運(yùn)行大型JavaScript應(yīng)用程序、在瀏覽器中異步加載資源、或者像文本模板(text templates)(那些載入器就像RequireJS所做的一樣)那樣包含資源等諸如此類的需求。

  正在考慮將模塊(Modules)作為Harmony/ECMAScript 6的語(yǔ)言級(jí)別功能。這多虧了模塊系統(tǒng)作者們的思想和過(guò)去幾年的辛勤工作,很可能我們最終得到的語(yǔ)言會(huì)適用于構(gòu)建現(xiàn)代JavaScript應(yīng)用程序。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多