圖文揭秘Node.js中exports和module.exports

概述

首先我們得先擺出兩條不變的真理:

  1. exports一開(kāi)始是指向module.exports的;
  2. 通過(guò)require得到的是module.exports中的內(nèi)容,而不是exports的內(nèi)容;

詳解

exports和module這兩個(gè)對(duì)象是所有Node.js類型的文件中都默認(rèn)隱式存在的,比如我們新建一個(gè)test.js文件:

console.log(exports);
console.log(module);

在終端運(yùn)行:

[qifuguang@Mac~/nodejs/learnModule]$ node test.js
{}
Module {
  id: '.',
  exports: {},
  parent: null,
  filename: '/Users/qifuguang/nodejs/learnModule/test.js',
  loaded: false,
  children: [],
  paths:
   [ '/Users/qifuguang/nodejs/learnModule/node_modules',
     '/Users/qifuguang/nodejs/node_modules',
     '/Users/qifuguang/node_modules',
     '/Users/node_modules',
     '/node_modules' ] }

可以看到,test.js文件中并未聲明exports和module對(duì)象,但是它們確實(shí)存在。并且可以看到,exports的初始值是{},而module的初始值有一大串屬性,其中還包含一個(gè)exports屬性,它的初始值也是{}。

實(shí)際上,一開(kāi)始exports就是指向module.exports的,引用關(guān)系如下圖:



請(qǐng)牢記這個(gè)引用圖,之后的分析都依靠這個(gè)圖。

我們?cè)倥e個(gè)例子,創(chuàng)建如下的my_module.js文件:

exports.sayHello = function() {
    console.log('Hello world!');
}

再在同一個(gè)目錄下創(chuàng)建app.js文件:

myModule = require('./my_module');
myModule.sayHello()

在終端運(yùn)行app.js:

[qifuguang@Mac~/nodejs/learnModule]$ node app.js
Hello world!

現(xiàn)在我們分析一下為什么會(huì)有這樣的輸出結(jié)果:
在app.js文件中我們使用require語(yǔ)句從my_module.js模塊中得到了module.exports,這里的module.exports的內(nèi)容是什么呢?

在my_module.js文件中我們?cè)趀xports的基礎(chǔ)上為它添加了一個(gè)屬性sayHello,這個(gè)屬性的值是一個(gè)函數(shù),并且因?yàn)槌跏紩r(shí),exports指向的是module.exports,他倆共享同一塊內(nèi)存,所以這個(gè)操作后,module.exports變成了這樣:



所以,app.js文件中的myModule變量的值為:

{
    sayHello: function() {console.log('Hello world');}
}

于是,很自然地,我們可以使用myModule.sayHello調(diào)用它對(duì)應(yīng)的函數(shù),輸出熟悉的Hello world字符串。

再舉個(gè)例子,我們將my_module.js文件修改為如下內(nèi)容:

exports = {
    sayHello: function() {console.log('Hello world!');}
}

然后將app.js文件修改為如下內(nèi)容:

myModule = require('./my_module');

console.log('module.exports:');
console.log(module.exports);

myModule.sayHello()

然后一樣在終端運(yùn)行:

[qifuguang@Mac~/nodejs/learnModule]$ node app.js
module.exports:
{}
/Users/qifuguang/nodejs/learnModule/app.js:6
myModule.sayHello()
         ^

TypeError: myModule.sayHello is not a function
    at Object.<anonymous> (/Users/qifuguang/nodejs/learnModule/app.js:6:10)
    at Module._compile (module.js:435:26)
    at Object.Module._extensions..js (module.js:442:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:311:12)
    at Function.Module.runMain (module.js:467:10)
    at startup (node.js:136:18)
    at node.js:963:3

可以看到,報(bào)錯(cuò)了,報(bào)錯(cuò)了,報(bào)錯(cuò)了!

分析一下原因:
my_module.js文件中將exports重新賦值為一個(gè)新的對(duì)象,這就相當(dāng)于Java中的

Object newObje = new Object();

一樣,這個(gè)時(shí)候exports將會(huì)自己分配一塊新的內(nèi)存,而不再指向module.exports了,所以這個(gè)時(shí)候exports和module.exports徹底斷絕關(guān)系,無(wú)論你怎么蹂躪(操作)exports對(duì)象,都與module.exports無(wú)關(guān)了。

所以,my_module.js文件中為exports對(duì)象重新賦值之后,exports和module.exports的狀態(tài)是這樣的:


從輸出中也可以看到,此時(shí)的module.exports={},所以肯定找不到sayHello函數(shù),那必然報(bào)錯(cuò)!

其他的我也不多說(shuō)了,根據(jù)這兩個(gè)例子與這兩幅圖,我相信更多的情況大家都會(huì)自己分析了。

聲明

本文首發(fā)于個(gè)人技術(shù)博客,轉(zhuǎn)載請(qǐng)注明出處,本文鏈接:http://qifuguang.me/2015/11/11/揭秘Node-js中exports和module-exports/

如果你喜歡我的文章,請(qǐng)關(guān)注我的微信訂閱號(hào):“機(jī)智的程序猿”,更多精彩,盡在其中:


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容