摘要:主要區(qū)別是需要在聲明模塊時指定所有的依賴,通過形參傳遞依賴到模塊內(nèi)容中。
前言
昨天,公司同事問了我如下一個問題:
說他在看一個插件時,看到了源碼結(jié)構(gòu)如截圖所示,他知道(function(){})()是一種立即執(zhí)行函數(shù),但是在截圖中,最后的那個圓括號里又寫了一個函數(shù)function($,ChineseDistricts){...},這個函數(shù)暫且稱為“匿名函數(shù)1”,function (factory){...}暫且稱為“”匿名函數(shù)2”,意思是不是:把匿名函數(shù)1傳入到匿名函數(shù)2的參數(shù)factory中,然后檢測當前環(huán)境。如果檢測到了全局環(huán)境中存在exports對象,則證明是node環(huán)境,如果是node環(huán)境,則用factory(require("jquery"), require("ChineseDistricts"))這個方法來執(zhí)行匿名函數(shù)1,因為Node是基于模塊的,所以在Node中要使用這個插件的話,必須用require()方法把匿名函數(shù)2中需要的參數(shù)"$"和"ChineseDistricts"以模塊的方式給引用進來?
看到截圖的代碼和它的疑問,請接著往下看......
一、兼容多種模塊規(guī)范(AMD,CMD,Node)的代碼在JavaScript模塊化開發(fā)中,為了讓同一個模塊可以運行在前后端,以及兼容多種模塊規(guī)范(AMD,CMD,Node),類庫開發(fā)者需要將類庫代碼包裝在一個閉包內(nèi)。
1.AMD規(guī)范
AMD,即“異步模塊定義”。主要實現(xiàn)比如: RequireJS。
其模塊引用方式如下:define(id?,dependencies?,factory);
其中,id及依賴是可選的。其與CommonJS方式相似的地方在于factory的內(nèi)容就是實際代碼的內(nèi)容,下面是一個簡單的例子:
define(function(){ var exports = {}; exports.say = function(){ alert("hello"); }; return exports; });
2.CMD規(guī)范
CMD規(guī)范,與AMD類似,區(qū)別主要在于定義模塊和依賴引入的地方。主要實現(xiàn)比如: SeaJS。
主要區(qū)別是:AMD需要在聲明模塊時指定所有的依賴,通過形參傳遞依賴到模塊內(nèi)容中。
define(["dep1","dep2"],function(dep1,dep2){ return function(){}; });
與AMD相比,CMD更接近Node對CommonJS規(guī)范的定義:
define(factory);
在依賴部分,CMD支持動態(tài)引入,如下:
define(function(require,exports,module){ // Todo });
require、exports、module通過形參傳遞給模塊,在需要依賴模塊時,隨時調(diào)用require引入即可。
3.CommonJS的模塊實現(xiàn)
CommonJS的模塊引用使用require,如下:
var http = require("http");
4.Node的模塊實現(xiàn)
在Node中引入模塊,需要經(jīng)過下面3個步驟:路徑分析;文件定位;編譯執(zhí)行。主要用require()方法來查找模塊。
5.兼容多種模塊規(guī)范
為了讓同一個模塊可以運行在前后端,在開發(fā)過程中需要考慮兼容前后端問題,以下代碼演示如何將hello()方法定義到不同的運行環(huán)境中,它能夠兼容AMD、CMD、Node以及常見的瀏覽器環(huán)境中:
;(function (name, definition) { // 檢測上下文環(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對象 this[name] = definition(); } })("hello", function () { var hello = function () {}; return hello; });二、如何封裝Node.js和前端通用的模塊
在Node.js中對模塊載入和執(zhí)行進行了包裝,使得模塊文件中的變量在一個閉包中,不會污染全局變量,和他人沖突。
前端模塊通常是我們開發(fā)人員為了避免和他人沖突才把模塊代碼放置在一個閉包中。
如何封裝Node.js和前端通用的模塊,我們可以參考Underscore.js 實現(xiàn),他就是一個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._ = _; }
通過判斷exports是否存在來決定將局部變量 _ 賦值給exports,向后兼容舊的require() API,如果在瀏覽器中,通過一個字符串標識符“_”作為一個全局對象;完整的閉包如下:
(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);
通過function定義構(gòu)建了一個閉包,call(this)是將function在this對象下調(diào)用,以避免內(nèi)部變量污染到全局作用域。瀏覽器中,this指向的是全局對象(window對象),將“_”變量賦在全局對象上“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));
再來看看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);
從上面的幾個例子可以看出,在封裝Node.js和前端通用的模塊時,可以使用以下邏輯:
if (typeof exports !== "undefined") { exports.** = **; } else { this.** = **; }
即,如果exports對象存在,則將局部變量裝載在exports對象上,如果不存在,則裝載在全局對象上。如果加上ADM規(guī)范的兼容性,那么多加一句判斷:
if (typeof define === "function" && define.amd){}
參考鏈接:
1.http://www.css88.com/archives...
2.http://www.css88.com/archives...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/89165.html
摘要:是另一種模塊化方案,它與很類似,不同點在于推崇依賴前置提前執(zhí)行,推崇依賴就近延遲執(zhí)行。 commonJS規(guī)范 隨著前端技術(shù)的不斷發(fā)展,項目越來越大,越來越不好管理,多人開發(fā)越來讓開發(fā)者頭疼,于是出現(xiàn)了命名空間,這沒有辦法的辦法,但是所有變量都是全局的話,管理非常混亂,而且在Node 出現(xiàn)前,javascript 在服務(wù)器端基本沒有市場,經(jīng)過javascript 不斷努力,社區(qū)為 jav...
摘要:模塊中定義的全局變量只作用于該文件內(nèi)部,不污染其他模塊。由純編寫的部分稱為內(nèi)建模塊,例等模塊部分使用編寫。兼容多種模塊規(guī)范檢測是否為或者檢測是否為或環(huán)境定義為普通模塊將模塊執(zhí)行結(jié)果掛載在對象下 1.為什么要CommonJS規(guī)范 javascript存在的缺點 沒有模塊系統(tǒng) 標準庫比較少 沒有標準接口 缺乏包管理系統(tǒng) CommonJS規(guī)范的提出,彌補了javascript沒有標準的缺...
摘要:它就是一套兼容方案,目前兼容的有以及原生支持。返回值問題在第一次使用時,。具體是什么意義呢的返回值,其實就是插件提供的對外接口,而實際上,就是一個對象。而在環(huán)境下,只需要將這個返回值賦予即可完成該模塊的接口。 有更新,請到github上看源碼 什么是OMD 在node.js流行起來之前,javascript的開發(fā)方式都是函數(shù)式的順序依賴關(guān)系,直到node火起來。CommonJS其實首先...
摘要:所有依賴這個模塊的語句,都定義在一個回調(diào)函數(shù)中,等到加載完成之后,這個回調(diào)函數(shù)才會運行。也采用語句加載模塊,但是不同于,它要求兩個參數(shù)第一個參數(shù),是一個數(shù)組,里面的成員就是要加載的模塊第二個參數(shù),則是加載成功之后的回調(diào)函數(shù)。 本篇文章來自對文章《js模塊化編程之徹底弄懂CommonJS和AMD/CMD!》的總結(jié),大部分摘自文章原話,本人只是為了學(xué)習(xí)方便做的筆記,之后有新的體會會及時補充...
摘要:規(guī)范則是非同步加載模塊,允許指定回調(diào)函數(shù),可以實現(xiàn)異步加載依賴模塊,并且會提前加載由于主要用于服務(wù)器編程,模塊文件一般都已經(jīng)存在于本地硬盤,所以加載起來比較快,不用考慮非同步加載的方式,所以規(guī)范比較適用。 JS模塊化 模塊化的理解 什么是模塊? 將一個復(fù)雜的程序依據(jù)一定的規(guī)則(規(guī)范)封裝成幾個塊(文件), 并進行組合在一起; 塊的內(nèi)部數(shù)據(jù)/實現(xiàn)是私有的, 只是向外部暴露一些接口(...
閱讀 987·2021-11-24 10:30
閱讀 2316·2021-10-08 10:04
閱讀 3949·2021-09-30 09:47
閱讀 1433·2021-09-29 09:45
閱讀 1435·2021-09-24 10:33
閱讀 6234·2021-09-22 15:57
閱讀 2351·2021-09-22 15:50
閱讀 4079·2021-08-30 09:45