1. 程式人生 > >node.js中exports與module.exports的區別分析

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()方法,這種問題的原因同樣是指向的記憶體區域發生變化所導致的。

總結

  1. exports物件是module物件的一個屬性,在初始時exports和module.exports是指向同一塊記憶體區域的;
  2. 在不改變exports記憶體指向的情況下,修改exports的值可以改變module.exports的值;
  3. 匯出儘量使用module.exports以避免混淆。