深入底层理解Node.js 中的module.exports 和 exports

71 阅读2分钟

在 Node.js 中,module.exportsexports 都用于向外部暴露模块的公共接口。要理解这两者的区别,首先需要了解 Node.js 的模块系统。

Node.js 中的每个文件都被视为一个独立的模块,每个模块都有自己的作用域。当你在一个模块中声明一个变量,那个变量只在那个模块内部可见,外部无法访问。如果你想让一个模块能够访问另一个模块中的变量,你就需要使用 module.exports 或者 exports

这两者的工作原理是这样的:

  • 每个模块都有一个 module 对象,module 对象有一个 exports 属性。这个 exports 属性就是我们在模块中 require 一个模块时得到的对象。

  • exports 实际上仅仅是对 module.exports 的一个全局引用,它们最初指向同一个对象。

所以当你要暴露一个模块的公共接口时,你可以选择直接给 module.exports 赋值,或者给 exports 添加属性。这两种方式的效果是一样的,因为 exportsmodule.exports 指向的是同一个对象。

但是,这里有一个重要的区别需要注意:

  • 如果你给 module.exports 赋值一个新的对象,那么 require 就会返回这个新的对象。而 exports 仍然指向原来的对象,此时对 exports 的任何修改都不会影响到 module.exports

  • 相反,如果你给 exports 赋值一个新的对象,那么 require 仍然会返回 module.exports 指向的原来的对象,因为 exports 不再指向 module.exports

这里解释一下上述两句话:

1.真正导出的是module.exports,exports只是前者的一个引用;即内部是module.expoerts = exports;(需要认真理解一下这句话)

2.也就是说一旦使用了module.exports = xxx;之后无论你再怎么操作exports都无效了,即module.exports = xxx出现,module.exports和exports就没有切断联系了。

3.注意:当执行exports = 某对象 时,这是切断了module.exports和exports的联系

因此,如果你想要暴露的是一个函数或者一个构造函数,你应该使用 module.exports。如果你想要暴露一个对象,你可以使用 module.exportsexports

这就是为什么我们经常在 Node.js 中看到这样的代码:

module.exports = function() {
  // ...
};

或者这样的代码:

exports.myFunction = function() {
  // ...
};

但是你很少会看到这样的代码:

exports = function() {
  // ...
};

因为这样的代码实际上并没有改变 module.exports,所以 require 时不会得到我们期望的结果。