node.js模塊化&commonJS規范
nodejs與commonjs
nodejs主要用於服務端編程,文件一般都能夠本地讀取速度較快,采用的是同步加載的commonjs規范。
關於commonjs:
- 每個文件都是封閉的一個模塊,模塊里定義的變量、函數、類都是私有的
- module代表當前模塊,module是封閉的,但它的exports屬性向外提供調用接口
- require加載模塊,讀取並執行一個js文件,然后返回該模塊的exports對象
- commonjs是同步加載的,因此模塊加載的順序嚴格按照代碼書寫的順序執行
- 模塊可以多次加載,但在第一次加載之后模塊會被編譯執行,放入緩存,后續的require直接從緩存里取值,模塊代碼不再編譯執行
require內部處理流程
- 檢查Module._cache是否緩存了指定模塊
- 如果緩存沒有的話,就創建一個新的module實例將它保存到緩存
- module.load()加載指定模塊
- 在解析的過程中如果發生異常,就從緩存中刪除該模塊
- 返回該模塊的module.exports
模塊化導出方式
- global.address = 'beijing';//導出全局變量,只要導入相應js文件即可調用
- module.exports = "str";//可以
- module.exports.msg = 'str'//可以
- exports.msg = 'str'//可以
- exports = 'str'//不行
關於最后一個導出方式為什么不行的說明:
module是封閉的模塊,屬性、函數都是私有的,僅對外提供module.exports屬性以供調用,而require加載函數的返回值永遠是module.exports屬性。
由於exports默認指向的是module.exports,如果采用exports={}的方式,exports不再指向module.exports.
因為exports引用關系改變不再指向module.exports,所以無法被require識別調用
模塊化原理
module的封閉性,模塊的隔離怎么實現?
利用JavaScript函數式編程的特性,采用自執行函數,以其局部作用域實現隔離。
模塊化的輸出怎么實現?
node在加載js文件之前先准備一個module對象
module{id:'jsfilename',exports:{}}
在加載的時候,把js文件加載進load函數,module作為load函數的形參傳進去,最后把js文件的對象保存在module.exports屬性中並返回
在require獲取module時,只要傳入對應的id找到對應的module,就可以在Node中拿到對應module的exports對象,完成模塊的輸入與輸出
代碼如下:
// 准備module對象:
var module = {
id: 'hello',
exports: {}
};
var load = function (module) {
// 讀取的hello.js代碼:
function greet(name) {
console.log('Hello, ' + name + '!');
}
module.exports = greet;
// hello.js代碼結束
return module.exports;
};
var exported = load(module);
// 保存module:
save(module, exported);
案例
// a.js
exports.x = 'a1';
console.log('a.js ', require('./b.js').x);
exports.x = 'a2';
// b.js
exports.x = 'b1';
console.log('b.js ', require('./a.js').x);
exports.x = 'b2';
// main.js
console.log('main.js ', require('./a.js').x);
console.log('main.js ', require('./b.js').x);
輸出: a1 b2 a2 b2
分析:
前提:commonjs模塊的加載是嚴格同步的且在第一次加載時編譯代碼並存入緩存,后續加載直接從緩存讀取
- 執行main.js第一行,首次加載並執行a.js並將a加入緩存
- a.js中require b.js,此時a被阻塞,需要等待b編譯加載並執行完成,此時a的緩存寫入了x為a1
- b.js中require a,由於a的已經被main.js第一次加載,此刻讀取緩存中a的exports為a1
- b.js打印a1,隨后b將x=b2寫入緩存,b首次加載完成,此時a.js等待完畢,將x=a2寫入緩存,a首次加載完畢
- main.js中,a打印a編譯、加載完畢之后x的值,a2。main.js第二行,由於b.js已經存在於緩存中,於是直接從緩存中讀取x為b2並打印
- 綜上,打印結果為a1,b2,a2,b2
Nodejs模塊
主要基於commonJS規范,包括內置模塊、自定義模塊、第三方模塊
內置模塊
node自身攜帶的模塊
例如:require('http');require('path');require('url');
自定義模塊
require函數的形參path中用相對路徑或者絕對路徑導入的模塊
在本地實現,通過module.exports導出
第三方模塊
主要是npm的一些開源的包,當然你也可以發布自己的包