node.js中exports與module.exports的區別分析
前言
關於Node.js中的exports和module.exports,很多時候都比較容易讓人混淆,弄不清楚兩者間的區別。那麼我們就從頭開始理清這兩者之間的關係。
來源
在開發Node.js應用的時候,很多模組都是需要引入才能使用,但是為什麼exports和module.exports我們沒有引用卻可以直接使用呢?
事實上,Node.js應用在編譯的過程中會對JavaScript檔案的內容進行頭尾的封裝。例如:
// hello.js
const hello = function () {
console.log('Hello world');
}
module.exports = {
hello
}
// 頭尾封裝後的js程式碼
(function (exports, require, module, __filename, __dirname) {
const hello = function () {
console.log('Hello world');
}
module.exports = {
hello
}
})
複製程式碼
在進行了頭尾封裝之後,各個模組之間進行了作用域隔離,避免了汙染全域性變數,同時可以使每個模組在不引入這些變數的情況下可以使用它們。這些變數依次為當前模組的exports屬性、require()方法、當前模組自身(module)、在檔案系統中的完整路徑、檔案目錄。
區別
按照Node.js的解釋,exports是module物件的一個屬性,那麼exports和module.exports應該是等價的。的確如初,初始化的exports和module.exports變數的值均為{},程式碼驗證:
// hello.js
const hello = function () {
console.log('Hello world');
}
console.log('初始值==========');
console.log(exports);
console.log(module.exports);
module.exports = {
hello
}
// 輸出結果
初始值==========
{}
{}
複製程式碼
可以發現,module物件的exports屬性和exports均指向一個空物件{},那麼在匯出物件的時候使用exports和module.exports有什麼區別呢?
我們在使用require()方法引入模組的時候,其實是引入了module.exports物件, exports只是module物件的exports的一個引用,我們可以通過修改exports所指向物件的值來協助修改module.exports的值。
- 使用exports匯出
const hello = function () {
console.log('Hello world');
}
exports.hello = {
hello
}
console.log('修改值==========');
console.log(exports);
console.log(module.exports);
// 輸出結果
修改值==========
{ hello: { hello: [Function: hello] } }
{ hello: { hello: [Function: hello] } }
複製程式碼
由於exports和module.exports指向同一塊記憶體區域,所以我們修改exports物件的資料,那麼module.exports也會隨之改變。
- 使用module.exports匯出
// hello.js
const hello = function () {
console.log('Hello world');
}
module.exports = {
hello
}
console.log('修改值==========');
console.log(exports);
console.log(module.exports);
// 輸出結果
修改值==========
{}
{ hello: [Function: hello] }
複製程式碼
你會發現修改後的exports依然是{},而module.exports的值已經改變,這是由於當你給module.exports是直接等於一個新的物件,那麼其將指向一塊新的記憶體區域,而此時exports指向的仍然是之前的記憶體區域,所以二者的值會不一樣,但是此時你在其他檔案內引入hello.js檔案,仍然可以呼叫hello()方法,這也說明了匯出的是module.exports而不是exports。
- 給exports直接賦值
// hello.js
const hello = function () {
console.log('Hello world');
}
exports = {
hello
}
console.log('修改值==========');
console.log(exports);
console.log(module.exports);
// 輸出結果
修改值==========
{ hello: [Function: hello] }
{}
複製程式碼
使用這種方法匯出在其他檔案呼叫hello方法即會報錯,因為該檔案模組匯出的物件為空,當然也不可能有hello()方法,這種問題的原因同樣是指向的記憶體區域發生變化所導致的。
總結
- exports物件是module物件的一個屬性,在初始時exports和module.exports是指向同一塊記憶體區域的;
- 在不改變exports記憶體指向的情況下,修改exports的值可以改變module.exports的值;
- 匯出儘量使用module.exports以避免混淆。