摘要:主要區(qū)別是需要在聲明模塊時(shí)指定所有的依賴,通過(guò)形參傳遞依賴到模塊內(nèi)容中。
前言
昨天,公司同事問(wèn)了我如下一個(gè)問(wèn)題:
說(shuō)他在看一個(gè)插件時(shí),看到了源碼結(jié)構(gòu)如截圖所示,他知道(function(){})()是一種立即執(zhí)行函數(shù),但是在截圖中,最后的那個(gè)圓括號(hào)里又寫(xiě)了一個(gè)函數(shù)function($,ChineseDistricts){...},這個(gè)函數(shù)暫且稱為“匿名函數(shù)1”,function (factory){...}暫且稱為“”匿名函數(shù)2”,意思是不是:把匿名函數(shù)1傳入到匿名函數(shù)2的參數(shù)factory中,然后檢測(cè)當(dāng)前環(huán)境。如果檢測(cè)到了全局環(huán)境中存在exports對(duì)象,則證明是node環(huán)境,如果是node環(huán)境,則用factory(require("jquery"), require("ChineseDistricts"))這個(gè)方法來(lái)執(zhí)行匿名函數(shù)1,因?yàn)镹ode是基于模塊的,所以在Node中要使用這個(gè)插件的話,必須用require()方法把匿名函數(shù)2中需要的參數(shù)"$"和"ChineseDistricts"以模塊的方式給引用進(jìn)來(lái)?
看到截圖的代碼和它的疑問(wèn),請(qǐng)接著往下看......
一、兼容多種模塊規(guī)范(AMD,CMD,Node)的代碼在JavaScript模塊化開(kāi)發(fā)中,為了讓同一個(gè)模塊可以運(yùn)行在前后端,以及兼容多種模塊規(guī)范(AMD,CMD,Node),類庫(kù)開(kāi)發(fā)者需要將類庫(kù)代碼包裝在一個(gè)閉包內(nèi)。
1.AMD規(guī)范
AMD,即“異步模塊定義”。主要實(shí)現(xiàn)比如: RequireJS。
其模塊引用方式如下:define(id?,dependencies?,factory);
其中,id及依賴是可選的。其與CommonJS方式相似的地方在于factory的內(nèi)容就是實(shí)際代碼的內(nèi)容,下面是一個(gè)簡(jiǎn)單的例子:
define(function(){ var exports = {}; exports.say = function(){ alert("hello"); }; return exports; });
2.CMD規(guī)范
CMD規(guī)范,與AMD類似,區(qū)別主要在于定義模塊和依賴引入的地方。主要實(shí)現(xiàn)比如: SeaJS。
主要區(qū)別是:AMD需要在聲明模塊時(shí)指定所有的依賴,通過(guò)形參傳遞依賴到模塊內(nèi)容中。
define(["dep1","dep2"],function(dep1,dep2){ return function(){}; });
與AMD相比,CMD更接近Node對(duì)CommonJS規(guī)范的定義:
define(factory);
在依賴部分,CMD支持動(dòng)態(tài)引入,如下:
define(function(require,exports,module){ // Todo });
require、exports、module通過(guò)形參傳遞給模塊,在需要依賴模塊時(shí),隨時(shí)調(diào)用require引入即可。
3.CommonJS的模塊實(shí)現(xiàn)
CommonJS的模塊引用使用require,如下:
var http = require("http");
4.Node的模塊實(shí)現(xiàn)
在Node中引入模塊,需要經(jīng)過(guò)下面3個(gè)步驟:路徑分析;文件定位;編譯執(zhí)行。主要用require()方法來(lái)查找模塊。
5.兼容多種模塊規(guī)范
為了讓同一個(gè)模塊可以運(yùn)行在前后端,在開(kāi)發(fā)過(guò)程中需要考慮兼容前后端問(wèn)題,以下代碼演示如何將hello()方法定義到不同的運(yùn)行環(huán)境中,它能夠兼容AMD、CMD、Node以及常見(jiàn)的瀏覽器環(huán)境中:
;(function (name, definition) { // 檢測(cè)上下文環(huán)境是否為AMD或CMD var hasDefine = typeof define === "function", // 檢查上下文環(huán)境是否為Node hasExports = typeof module !== "undefined" && module.exports; if (hasDefine) { // AMD環(huán)境或CMD環(huán)境 define(definition); } else if (hasExports) { // 定義為普通Node模塊 module.exports = definition(); } else { // 將模塊的執(zhí)行結(jié)果掛在window變量中,在瀏覽器中this指向window對(duì)象 this[name] = definition(); } })("hello", function () { var hello = function () {}; return hello; });二、如何封裝Node.js和前端通用的模塊
在Node.js中對(duì)模塊載入和執(zhí)行進(jìn)行了包裝,使得模塊文件中的變量在一個(gè)閉包中,不會(huì)污染全局變量,和他人沖突。
前端模塊通常是我們開(kāi)發(fā)人員為了避免和他人沖突才把模塊代碼放置在一個(gè)閉包中。
如何封裝Node.js和前端通用的模塊,我們可以參考Underscore.js 實(shí)現(xiàn),他就是一個(gè)Node.js和前端通用的功能函數(shù)模塊,查看代碼:
// Create a safe reference to the Underscore object for use below. var _ = function(obj) { if (obj instanceof _) return obj; if (!(this instanceof _)) return new _(obj); this._wrapped = obj; }; // Export the Underscore object for **Node.js**, with // backwards-compatibility for the old `require()` API. If we"re in // the browser, add `_` as a global object via a string identifier, // for Closure Compiler "advanced" mode. if (typeof exports !== "undefined") { if (typeof module !== "undefined" && module.exports) { exports = module.exports = _; } exports._ = _; } else { root._ = _; }
通過(guò)判斷exports是否存在來(lái)決定將局部變量 _ 賦值給exports,向后兼容舊的require() API,如果在瀏覽器中,通過(guò)一個(gè)字符串標(biāo)識(shí)符“_”作為一個(gè)全局對(duì)象;完整的閉包如下:
(function() { // Baseline setup // -------------- // Establish the root object, `window` in the browser, or `exports` on the server. var root = this; // Create a safe reference to the Underscore object for use below. var _ = function(obj) { if (obj instanceof _) return obj; if (!(this instanceof _)) return new _(obj); this._wrapped = obj; }; // Export the Underscore object for **Node.js**, with // backwards-compatibility for the old `require()` API. If we"re in // the browser, add `_` as a global object via a string identifier, // for Closure Compiler "advanced" mode. if (typeof exports !== "undefined") { if (typeof module !== "undefined" && module.exports) { exports = module.exports = _; } exports._ = _; } else { root._ = _; } }).call(this);
通過(guò)function定義構(gòu)建了一個(gè)閉包,call(this)是將function在this對(duì)象下調(diào)用,以避免內(nèi)部變量污染到全局作用域。瀏覽器中,this指向的是全局對(duì)象(window對(duì)象),將“_”變量賦在全局對(duì)象上“root._”,以供外部調(diào)用。
和Underscore.js 類似的Lo-Dash,也是使用了類似的方案,只是兼容了AMD模塊載入的兼容:
;(function() { /** Used as a safe reference for `undefined` in pre ES5 environments */ var undefined; /** Used to determine if values are of the language type Object */ var objectTypes = { "boolean": false, "function": true, "object": true, "number": false, "string": false, "undefined": false }; /** Used as a reference to the global object */ var root = (objectTypes[typeof window] && window) || this; /** Detect free variable `exports` */ var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports; /** Detect free variable `module` */ var freeModule = objectTypes[typeof module] && module && !module.nodeType && module; /** Detect the popular CommonJS extension `module.exports` */ var moduleExports = freeModule && freeModule.exports === freeExports && freeExports; /*--------------------------------------------------------------------------*/ // expose Lo-Dash var _ = runInContext(); // some AMD build optimizers, like r.js, check for condition patterns like the following: if (typeof define == "function" && typeof define.amd == "object" && define.amd) { // Expose Lo-Dash to the global object even when an AMD loader is present in // case Lo-Dash was injected by a third-party script and not intended to be // loaded as a module. The global assignment can be reverted in the Lo-Dash // module by its `noConflict()` method. root._ = _; // define as an anonymous module so, through path mapping, it can be // referenced as the "underscore" module define(function() { return _; }); } // check for `exports` after `define` in case a build optimizer adds an `exports` object else if (freeExports && freeModule) { // in Node.js or RingoJS if (moduleExports) { (freeModule.exports = _)._ = _; } // in Narwhal or Rhino -require else { freeExports._ = _; } } else { // in a browser or Rhino root._ = _; } }.call(this));
再來(lái)看看Moment.js的封裝閉包主要代碼:
(function (undefined) { var moment; // check for nodeJS var hasModule = (typeof module !== "undefined" && module.exports); /************************************ Exposing Moment ************************************/ function makeGlobal(deprecate) { var warned = false, local_moment = moment; /*global ender:false */ if (typeof ender !== "undefined") { return; } // here, `this` means `window` in the browser, or `global` on the server // add `moment` as a global object via a string identifier, // for Closure Compiler "advanced" mode if (deprecate) { this.moment = function () { if (!warned && console && console.warn) { warned = true; console.warn( "Accessing Moment through the global scope is " + "deprecated, and will be removed in an upcoming " + "release."); } return local_moment.apply(null, arguments); }; } else { this["moment"] = moment; } } // CommonJS module is defined if (hasModule) { module.exports = moment; makeGlobal(true); } else if (typeof define === "function" && define.amd) { define("moment", function (require, exports, module) { if (module.config().noGlobal !== true) { // If user provided noGlobal, he is aware of global makeGlobal(module.config().noGlobal === undefined); } return moment; }); } else { makeGlobal(); } }).call(this);
從上面的幾個(gè)例子可以看出,在封裝Node.js和前端通用的模塊時(shí),可以使用以下邏輯:
if (typeof exports !== "undefined") { exports.** = **; } else { this.** = **; }
即,如果exports對(duì)象存在,則將局部變量裝載在exports對(duì)象上,如果不存在,則裝載在全局對(duì)象上。如果加上ADM規(guī)范的兼容性,那么多加一句判斷:
if (typeof define === "function" && define.amd){}
參考鏈接:
1.http://www.css88.com/archives...
2.http://www.css88.com/archives...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/89165.html
摘要:是另一種模塊化方案,它與很類似,不同點(diǎn)在于推崇依賴前置提前執(zhí)行,推崇依賴就近延遲執(zhí)行。 commonJS規(guī)范 隨著前端技術(shù)的不斷發(fā)展,項(xiàng)目越來(lái)越大,越來(lái)越不好管理,多人開(kāi)發(fā)越來(lái)讓開(kāi)發(fā)者頭疼,于是出現(xiàn)了命名空間,這沒(méi)有辦法的辦法,但是所有變量都是全局的話,管理非?;靵y,而且在Node 出現(xiàn)前,javascript 在服務(wù)器端基本沒(méi)有市場(chǎng),經(jīng)過(guò)javascript 不斷努力,社區(qū)為 jav...
摘要:模塊中定義的全局變量只作用于該文件內(nèi)部,不污染其他模塊。由純編寫(xiě)的部分稱為內(nèi)建模塊,例等模塊部分使用編寫(xiě)。兼容多種模塊規(guī)范檢測(cè)是否為或者檢測(cè)是否為或環(huán)境定義為普通模塊將模塊執(zhí)行結(jié)果掛載在對(duì)象下 1.為什么要CommonJS規(guī)范 javascript存在的缺點(diǎn) 沒(méi)有模塊系統(tǒng) 標(biāo)準(zhǔn)庫(kù)比較少 沒(méi)有標(biāo)準(zhǔn)接口 缺乏包管理系統(tǒng) CommonJS規(guī)范的提出,彌補(bǔ)了javascript沒(méi)有標(biāo)準(zhǔn)的缺...
摘要:它就是一套兼容方案,目前兼容的有以及原生支持。返回值問(wèn)題在第一次使用時(shí),。具體是什么意義呢的返回值,其實(shí)就是插件提供的對(duì)外接口,而實(shí)際上,就是一個(gè)對(duì)象。而在環(huán)境下,只需要將這個(gè)返回值賦予即可完成該模塊的接口。 有更新,請(qǐng)到github上看源碼 什么是OMD 在node.js流行起來(lái)之前,javascript的開(kāi)發(fā)方式都是函數(shù)式的順序依賴關(guān)系,直到node火起來(lái)。CommonJS其實(shí)首先...
摘要:所有依賴這個(gè)模塊的語(yǔ)句,都定義在一個(gè)回調(diào)函數(shù)中,等到加載完成之后,這個(gè)回調(diào)函數(shù)才會(huì)運(yùn)行。也采用語(yǔ)句加載模塊,但是不同于,它要求兩個(gè)參數(shù)第一個(gè)參數(shù),是一個(gè)數(shù)組,里面的成員就是要加載的模塊第二個(gè)參數(shù),則是加載成功之后的回調(diào)函數(shù)。 本篇文章來(lái)自對(duì)文章《js模塊化編程之徹底弄懂CommonJS和AMD/CMD!》的總結(jié),大部分摘自文章原話,本人只是為了學(xué)習(xí)方便做的筆記,之后有新的體會(huì)會(huì)及時(shí)補(bǔ)充...
摘要:規(guī)范則是非同步加載模塊,允許指定回調(diào)函數(shù),可以實(shí)現(xiàn)異步加載依賴模塊,并且會(huì)提前加載由于主要用于服務(wù)器編程,模塊文件一般都已經(jīng)存在于本地硬盤(pán),所以加載起來(lái)比較快,不用考慮非同步加載的方式,所以規(guī)范比較適用。 JS模塊化 模塊化的理解 什么是模塊? 將一個(gè)復(fù)雜的程序依據(jù)一定的規(guī)則(規(guī)范)封裝成幾個(gè)塊(文件), 并進(jìn)行組合在一起; 塊的內(nèi)部數(shù)據(jù)/實(shí)現(xiàn)是私有的, 只是向外部暴露一些接口(...
閱讀 1122·2021-11-24 10:30
閱讀 2457·2021-10-08 10:04
閱讀 4130·2021-09-30 09:47
閱讀 1631·2021-09-29 09:45
閱讀 1601·2021-09-24 10:33
閱讀 6444·2021-09-22 15:57
閱讀 2468·2021-09-22 15:50
閱讀 4203·2021-08-30 09:45