目前使用js變成離不開模塊,而如今最爲常見的模塊也就是node採用的COMMONjs的方式和es6規範,這裏對兩種的使用進行對比,並無深刻源碼盡情扣,html
node的模塊是比較常見的,是全局變量global中的一個屬性,文件和模塊是一一對應的(每一個文件被視爲一個獨立的模塊)。node
目前比較規範的是一個文件就是一個模塊,主要是exports和require進行處理,react
exports 變量是在模塊的文件級別做用域內有效的,它在模塊被執行前被賦於 module.exports 的值。它有一個快捷方式,以便 module.exports.f = ... 能夠被更簡潔地寫成 exports.f = ...。 注意,就像任何變量,若是一個新的值被賦值給 exports,它就再也不綁定到 module.exports。能夠具體看看代碼:es6
function add(x, y){ return x+y; } function multiply(x, y){ return x*y; } exports.add = (x, y) => x+y; //exports做爲module.exports的快捷方式 exports.multiply = (x, y) => x*y; module.exports = add; //此時exports module.exports重新被賦爲新對象或者函數(函數也是對象) exports = {add: add} //這是exports已經和module.exports沒有關係了,成了獨立的模塊做用域內對象,並不會被導出 module.exports = { //最後的導出部分 add: add, multiply: multiply };
關於exports和module.exports的關係看下面代碼的相似實現,好像實參和形參同樣,不一樣的是最後若是module.exports的沒有直接複製操做,會被賦值爲exports。express
function require(/* ... */) { const module = { exports: {} }; ((module, exports) => { // 模塊代碼在這。在這個例子中,定義了一個函數。 function someFunc() {} exports = someFunc; // 此時,exports 再也不是一個 module.exports 的快捷方式, // 且這個模塊依然導出一個空的默認對象。 module.exports = someFunc; // 此時,該模塊導出 someFunc,而不是默認對象。 })(module, module.exports); return module.exports; }
定義好了一個模塊就應該使用它,編程
var test = require('./moduleTest.js'); console.log(test(1, 2)); console.log(mutiply(1, 2));
使用是比較簡單的,使用變量獲取導出的對象exports,就可使用對象裏面的方法了,咱們有的時候可能會遇到這樣的狀況,無需使用變量接受模塊的exports,既能夠直接使用,這種狀況通常是在模塊代碼導出的時候作了處理:api
module.exports.add = global.add = (x, y) => x+y;
把模塊的方法直接提高到全局做用域,這其實並非好的選擇。緩存
Node 使用兩個核心模塊來管理模塊依賴:模塊化
require 模塊,是個看起來像在全局做用域有效的模塊——不須要 require('require')。函數
module 模塊,看起來也像是在全局做用域內有效——不須要 require('module')。
由 require 模塊導出的主要對象是一個函數(如上例所用)。 當 Node 使用本地文件路徑做爲函數的惟一參數調用該 require() 函數時,Node 將執行如下步驟:
解析:找到文件的絕對路徑。
加載:肯定文件內容的類型.
封裝:給文件其私有做用域。 這使得 require 和 module 對象二者均可如下載咱們須要的每一個文件。
評估:這是 VM 對加載的代碼最後須要作的。
緩存:當咱們再次須要這個文件時,再也不重複全部的步驟。
模塊在第一次加載後會被緩存。 這也意味着(相似其餘緩存機制)若是每次調用 require('foo') 都解析到同一文件,則返回相同的對象。
屢次調用 require(foo) 不會致使模塊的代碼被執行屢次。 這是一個重要的特性。 藉助它, 能夠返回「部分完成」的對象,從而容許加載傳遞的依賴, 即便它們會致使循環。
模塊是基於其解析的文件名進行緩存的。 因爲調用模塊的位置的不一樣,模塊可能被解析成不一樣的文件名(好比從 node_modules 目錄加載),這樣就不能保證 require('foo') 總能返回徹底相同的對象。
此外,在不區分大小寫的文件系統或操做系統中,被解析成不一樣的文件名能夠指向同一文件,但緩存仍然會將它們視爲不一樣的模塊,並屢次從新加載。 例如,require('./foo') 和 require('./FOO') 返回兩個不一樣的對象,而不會管 ./foo 和 ./FOO 是不是相同的文件。
在ES6以前,要使用一個模塊,必須使用require函數將一個模塊引入,但ES6並無採用這種模塊化方案,在ES6中使用import指令引入一個模塊或模塊中的部分接口,並無將require寫入標準,這也就是說require對於ES6代碼而言,只是一個普通函數。
同理,在ES6標準中,導出模塊的接口也只能使用export指令,而非exports對象,這也一樣意味着module.exports只是node,requirejs等模塊化庫的自定義變量,而非ES標準接口。
固然,ES6的模塊化也很複雜,不僅模塊接口的導入導出這點東西,只不過本文無恥的只討論這個相對而言比較常見的問題。
先列舉一些經常使用的使用方法:
export function fun() {}; export { name1, name2, …, nameN }; export { variable1 as name1, variable2 as name2, …, nameN }; export let name1, name2, …, nameN; // also var export let name1 = …, name2 = …, …, nameN; // also var, const export default expression; export default function (…) { … } // also class, function* export default function name1(…) { … } // also class, function* export { name1 as default, … }; export * from …; export { name1, name2, …, nameN } from …; export { import1 as name1, import2 as name2, …, nameN } from …;
導出的不是變量是絕對錯誤的,包括導出表達式,也是絕對錯誤的,export容許屢次export {}這種形式,看上去很奇怪,其實和react的setState同樣,是一個增量的添加模式。另外,ES6更厲害之處在於,能夠在export變量以後,繼續修改變量。
import的使用其實比require更加的舒服
import {a,obj} from './module-file';
和node以前的require不同,require只能把模塊放到一個變量中,而在ES6中,擁有對象解構賦值的能力,因此直接就把引入的模塊的接口賦值給變量了。內在機理也有不一樣,require須要去執行整個模塊,將整個模塊放到內存中(也就是咱們說的運行時),若是隻是使用到其中一個方法,性能上就差不少,而import...from則是隻加載須要的接口方法,其餘方法在程序啓動以後根本觸及不到,因此這種又被稱爲「編譯時」,性能上好不少。
編程的同窗對as都容易理解,簡單的說就是取一個別名。上面export中能夠用,import中其實也能夠用:
// a.js var a = function() {}; export {a as fun}; // b.js import {fun as a} from './a'; a();
而default則是一種語法糖,實踐相似:
// d.js export default function() {} // 等效於: function a() {}; export {a as default}; //在import的時候,能夠這樣用: import a from './d'; // 等效於,或者說就是下面這種寫法的簡寫,是同一個意思 import {default as a} from './d';
則是表明把全部的export賦值給 as newExport,這個後面跟這個的別名對象,從而獲取模塊全部方法。
參考:http://www.tangshuang.net/288...
https://juejin.im/entry/58d4e...
http://nodejs.cn/api/modules....