NodeJs 中 exports 和 module.exports 的关系

首先要说明两点:

  1. exports 是默认指向 module.exports 的,即 exports 是对 module.exports 的一个引用;
  2. 通过 require 引用模块后,返回给调用者的是 module.exports 中的内容而不是 exports 的内容;

接下来具体看一下他们的关系:
exports 和 module 这两个对象是所有 Node.js 类型的文件中都默认隐式存在的,比如我们新建一个 test.js 文件:

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

在终端运行,输出的结果如下:

[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 对象,但是它们确实存在。并且可以看到,exports 的初始值是{},而 module 的初始值有一大串属性,其中还包含一个 exports 属性,它的初始值也是{}。

实际上,一开始 exports 就是指向 module.exports 的,引用关系如下图:

exports和module的引用图.png

请牢记这个引用图,之后的分析都依靠这个图。

我们再举个例子,创建如下的 my_module.js 文件:

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

再在同一个目录下创建 app.js 文件:

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

在终端运行 app.js:

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

现在我们分析一下为什么会有这样的输出结果:
在 app.js 文件中我们使用 require 语句从 my_module.js 模块中得到了module.exports,这里的 module.exports 的内容是什么呢?

在 my_module.js 文件中我们在 exports 的基础上为它添加了一个属性sayHello,这个属性的值是一个函数,并且因为初始时,exports 指向的是module.exports,他俩共享同一块内存,所以这个操作后,module.exports 变成了这样:

exports赋值之后.png

所以,app.js 文件中的 myModule 变量的值为:

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

于是,很自然地,我们可以使用 myModule.sayHello 调用它对应的函数,输出熟悉的 Hello world 字符串。

再举个例子,我们将 my_module.js 文件修改为如下内容:

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

然后将 app.js 文件修改为如下内容:

myModule = require('./my_module');
console.log('module.exports:');
console.log(module.exports);
myModule.sayHello()

然后一样在终端运行:

[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

可以看到,报错了,报错了,报错了!

分析一下原因:
my_module.js 文件中将 exports 重新赋值为一个新的对象,这就相当于 Java 中的 Object newObje = new Object(); 一样,这个时候 exports 将会自己分配一块新的内存,而不再指向 module.exports 了,所以这个时候 exports 和 module.exports 彻底断绝关系,无论你怎么蹂躏(操作) exports 对象,都与 module.exports 无关了。

所以,my_module.js 文件中为 exports 对象重新赋值之后,exports 和 module.exports 的状态是这样的:

exports重新分配内存之后.png

从输出中也可以看到,此时的 module.exports={},所以肯定找不到 sayHello 函数,那必然报错!

其他的我也不多说了,根据这两个例子与这两幅图,我相信更多的情况大家都会自己分析了。

原文:http://qifuguang.me/2015/11/11/%E6%8F%AD%E7%A7%98Node-js%E4%B8%ADexports%E5%92%8Cmodule-exports/

发表评论
* 昵称
* Email
* 网址
* 评论