Node應用由模塊組成,採用CommonJS模塊規範。node
根據這個規範,每一個文件就是一個模塊,有本身的做用域。在一個文件裏面定義的變量、函數、類,都是私有的,對其餘文件不可見。函數
CommonJS規範規定,每一個模塊內部,module變量表明當前模塊。這個變量是一個對象,它的exports屬性(即module.exports)是對外的接口。加載某個模塊,實際上是加載該模塊的module.exports屬性。ui
var x = 5; var addX = function (value) { return value + x; }; module.exports.x = x; module.exports.addX = addX;
上面代碼經過module.exports輸出變量x和函數addX。spa
require方法用於加載模塊。code
var example = require('./example.js'); console.log(example.x); // 5 console.log(example.addX(1)); // 6
爲了方便,Node爲每一個模塊提供一個exports變量,指向module.exports。這等同在每一個模塊頭部,有一行這樣的命令。對象
var exports = module.exports;
因而咱們能夠直接在 exports 對象上添加方法,表示對外輸出的接口,如同在module.exports上添加同樣。注意,不能直接將exports變量指向一個值,由於這樣等於切斷了exports與module.exports的聯繫。blog
module.exports
對象是由模塊系統建立的。 有時這是難以接受的;許多人但願他們的模塊成爲某個類的實例。 爲了實現這個,須要將指望導出的對象賦值給 module.exports
。 注意,將指望的對象賦值給 exports
會簡單地從新綁定本地 exports
變量,這可能不是指望的。接口
例子,假設建立了一個名爲 a.js
的模塊:事件
const EventEmitter = require('events'); module.exports = new EventEmitter(); // 處理一些工做,並在一段時間後從模塊自身觸發 'ready' 事件。 setTimeout(() => { module.exports.emit('ready'); }, 1000);
而後,在另外一個文件中能夠這麼作:內存
const a = require('./a'); a.on('ready', () => { console.log('模塊 a 已準備好'); });
注意,對 module.exports
的賦值必須當即完成。 不能在任何回調中完成。 如下是無效的:
x.js:
setTimeout(() => { module.exports = { a: 'hello' }; }, 0);
y.js:
const x = require('./x');
console.log(x.a);
exports
變量是在模塊的文件級別做用域內有效的,它在模塊被執行前被賦予 module.exports
的值。
它有一個快捷方式,以便 module.exports.f = ...
能夠被更簡潔地寫成 exports.f = ...
。 注意,就像任何變量,若是一個新的值被賦值給 exports
,它就再也不綁定到 module.exports
:
module.exports.hello = true; // 從對模塊的引用中導出 exports = { hello: false }; // 不導出,只在模塊內有效
當 module.exports
屬性被一個新的對象徹底替代時,也會從新賦值 exports
,例如:
module.exports = exports = function Constructor() { // ... 及其餘 };
爲了解釋這個行爲,想象對 require()
的假設實現,它跟 require()
的實際實現至關相似:
function require(/* ... */) { const module = { exports: {} }; ((module, exports) => { // 模塊代碼在這。在這個例子中,定義了一個函數。 function someFunc() {} exports = someFunc; // 此時,exports 再也不是一個 module.exports 的快捷方式, // 且這個模塊依然導出一個空的默認對象。 module.exports = someFunc; // 此時,該模塊導出 someFunc,而不是默認對象。 })(module, module.exports); return module.exports; }
demo.js:
console.log(exports); // {} console.log(module.exports); // {} console.log(exports === module.exports); // true console.log(exports == module.exports); // true console.log(module); /** Module { id: '.', exports: {}, parent: null, filename: '/Users/ring/Desktop/demo.js', loaded: false, children: [], paths: [ '/Users/ring/Desktop/node_modules', '/Users/ring/node_modules', '/Users/node_modules', '/node_modules' ] } */
注意
每一個js文件一建立,都有一個var exports = module.exports = {}
, 使exports和module.exports
都指向一個空對象。
module.exports
和exports所指向的內存地址相同