先來看 module.exports
。假設一個 node.js 模組名稱為 a.js,內容如下:
// a.js
module.exports = '123';
這個模組很單純,會回傳字串 '123'。如果在 b.js 中要調用,執行:
// b.js
var a = require('./a.js'); // a 為 123;
所以以白話講,module.exports
所被賦予的值,就會成為 require
之後的回傳值。
exports vs module.exports
這兩者不同的地方,可以從 require
這個方法來看:
require
這個方法會實體化一個叫 Module
的 Class,並自我執行一個匿名函式。
// 這段示意 "require('./a.js')" 會做的事
// Module 的建構式,
// Module 擁有一些屬性,其中一個是 'exports'
function Module() {
// other setup
this.exports = {};
}
var module = new Module();
(function(exports, require, module){
// a.js 的內容
module.exports = '123';
})(module.exports, require, module);
注意到 a.js 的內容是在該自動執行的匿名函式中執行的,所以可以看到在 a.js 的 closure 中,會有三個變數 exports
、require
及 module
,也就是在定義模組時很常用到的三個關鍵字。
從自動執行匿名函式丟入的參數 (module.exports, require, module)
可以知道 a.js closure 中的 exports,就等於 module.exports。
因為 exports
指向 module.exports
,所以要 expose 屬性就不必打 module.exports,簡化為 exports 就好:
// a.js
exports.name = 'jcc';
// 上述其實等於 module.exports = 'jcc';
但是如果在 a.js
中改變 exports
的指向,就會喪失對 module.exports
的指向。此時對 exports
下屬性就不會作用在 module.exports
,這不會是你要的:
// a.js
exports = {}; // exports 指向到一個空物件
exports.value = 'a value';
// b.js
var a = require('./a.js');
console.log(a.value); // undefined
因此會有人說*「記得重新把 exports 指向 module.exports」*:
// a.js
exports = module.exports = {};
exports.value = 'a value';
// b.js
var a = require('./a.js');
console.log(a.value); // 'a value'
上面範例強力參考了 stackoverflow 上的這個回答,如果這邊看不懂可以去看看原文。
無論如何,如果你覺得一直用 exports = module.exports
卻不懂背後原理的,希望可以幫助到你。
輸出 Class 及靜態方法
利用上面的方法就可以實作這個功能,先把 exports
重新指向到 module.exports
以及 Class 的建構式,接著再把靜態方法指定在 exports
變數上:
// personFactory.js
var counts = 0;
exports = module.exports = function(name){
counts++;
this.name = name;
this.rocks = function() {
console.log(this.name + ' rocks!');
}
};
exports.counts = function() {
console.log(counts + ' ppl is created');
};
// index.js
var PersonFactory = require('./personFactory');
var jcc = new PersonFactory('jcc');
jcc.rocks(); // log 'jcc rocks'
PersonFactory.counts() // log '1 ppl is created'
這邊需要了解一個情況:可以對函式附加屬性。所以透過這樣可以產生靜態方法。