引
在node.js中我們可以使用module.exports和exports導出模塊,設置導出函數、數組、變量等等
為什么可以用這兩個模塊?
或者直接問,node.js的模塊功能是怎么實現的。
這樣得益于javascript是函數性的語言,并支持閉包。
js的閉包
直接看w3cschool吧,感覺講的挺好的:js閉包
node.js的模塊實現,大致代碼
首先準備一個nodejs規范的代碼:
hello.js
var s = 'Hello';
var name = 'world';console.log(s + ' ' + name + '!');
Node.js加載了hello.js后,它把這段代碼包裝一下,大概變成這樣:
var module = {id: 'hello',exports: {}
};
var load = function () {// 實際我們自己編寫的hello.js代碼:function greet(name) {console.log('Hello, ' + name + '!');}module.exports = greet;// hello.js代碼結束return module.exports;
};
var exports = load();
// 保存module:
save(module, exports);
module是nodejs自動加的一個對象,可見,初始化的時候會先對module.exports賦值一個空的對象{}。
save(module, exports);這個函數是個真·全局函數,作用是把exports這個變量存到某個全局變量中。其它模塊通過require()函數實際上就是去這個全局變量里把對應的值拿出來。
這樣,看js代碼大概就明白了,為什么在nodejs里可以直接用module.exports和exports這兩個語法。
module.exports和exports
module.exports和exports實際上都是對一個對象的引用,這個對象初始化就是一個空對象{}。所以直接就可以使用類似
示例一:
module.exports.foo = function () { return 'foo'; };
module.exports.bar = function () { return 'bar'; };
或者示例二:
exports.foo = function () { return 'foo'; };
exports.bar = function () { return 'bar'; };
這兩個示例作用是一樣的,其本質都是往最開始初始化的空數組里添加成員。
示例三
module.exports = {hello: hello,greet: greet
};
示例三就不一樣了,實際上module.exports重新引用到了一個新的對象里。如果示例三前面有示例一或者二的代碼,那么會最終導致示例一或者二導出的模塊丟掉。
示例四
exports = {hello: hello,greet: greet
};
示例四看上去雖然和示例三差不多,但是這種寫法實際上并沒有輸出任何變量!注意看nodejs的實現代碼,load()函數里最后return的是module.exports,也就是說最后save的是module.exports的引用對象,而示例四中exports被賦值了一個新的對象,此時module.exports和exports引用的已經不是同一個對象了!
那么提問:示例三雖然對module.exports重新引用到了一個新的對象,最終結果也能實現模塊的正常導出,那么示例三里的exports此時引用的是什么對象呢?
最后,如果你打算導出一個數組或者變量,或者函數,都會涉及到module.exports原引用對象的丟棄,要額外注意,此時要小心不要丟掉前面已經導出的模塊。
結論
如果要輸出一個鍵值對象{},可以利用exports這個已存在的空對象{},并繼續在上面添加新的鍵值;
如果要輸出一個函數或數組,必須直接對module.exports對象賦值。
所以我們可以得出結論:直接對module.exports賦值,可以應對任何情況:
module.exports = {foo: function () { return 'foo'; }
};
或者:
module.exports = function () { return 'foo'; };
最終,我們強烈建議使用module.exports = xxx的方式來輸出模塊變量,這樣,你只需要記憶一種方法。
或者使用我喜歡的方法,對空對象直接添加值:
var foo = function () { return 'foo'; };...module.exports.foo = foo;//
module.exports.bar = function () { return 'bar'; };
參考
liaoxuefeng
exports 和 module.exports 的區別
w3cschool
JS是按值傳遞還是按引用傳遞?,但是我不是很同意文中所說的按共享傳遞這名字的叫法。實際上無論傳遞普通變量還是傳遞函數,都是按值拷貝傳遞,只不過傳遞對象的時候,拷貝過去的是個引用變量罷了,即引用副本。
這里就是c語言指針的思想。
如果你對我上面所講的還不明白,建議去把c語言里的指針好好的重學一遍。真正理解透徹了c里面的指針,學起來其它所有語言都不怕。