Node.js 中 module.exports 与 exports 有什么区别

module.exports 是模块真正导出的对象,exports 只是它的引用别名。给 exports 添加属性等价于修改 module.exports,但直接给 exports 重新赋值会断开引用导致导出失效。导出函数或对象请直接赋值 module.exports。

最佳实践
Node.js 事件循环与异步任务示意

module.exports 是 require() 真正拿到的对象;exports 只是指向它的快捷引用。 给 exports 挂属性(exports.foo = ...)等价于 module.exports.foo = ...;但直接整体赋值 exports = function(){} 会断开两者的引用关系,导出不会生效——整体导出必须用 module.exports = ...。

两种正确用法

// 方式一:exports 挂属性(导出多个成员)
exports.getUser = function() { /* ... */ };
exports.createUser = function() { /* ... */ };

// 方式二:module.exports 整体赋值(导出函数/类/对象)
module.exports = function() {
  console.log('Hello');
};

常见错误

// 错误!exports 被重新赋值后不再指向 module.exports
exports = function() { console.log('不会被导出'); };

require 这个模块得到的仍是空对象 {},因为模块系统返回的是 module.exports,而你只改了局部变量 exports 的指向。

原理一句话

每个模块加载时 Node 大致执行了 var exports = module.exports = {};,最后 return module.exports。所以:

  • 改属性 → 两者都看到(同一对象)✅
  • 整体替换 → 只有 module.exports = 有效 ✅
  • exports = 整体替换 → 断开引用,无效 ❌

常见问题(FAQ)

Q:两种写法能混用吗?

不建议。如果最终执行了 module.exports = {...},之前通过 exports 挂的属性全部作废(旧对象被丢弃)。一个模块统一用一种风格。

Q:ES Module 里有这个问题吗?

没有。ESM 用 export / export default,语法层面就是静态导出声明,不存在引用陷阱。新代码建议直接用 ESM。

Q:怎么导出既包含函数又包含属性?

给函数对象挂属性:module.exports = main; module.exports.helper = helper;。

参考资料

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台