在之前的文章中,粗略谈到了Node 中 exports 与 module.exports 有什么区别,本文将展开讨论,更详细地补充两者的区别。
一、两者的关联本质
在JS文件启动时,Node.js 开始执行文件,会给这个文件内生成一个 exports 和 module 对象,而module又有一个 exports 属性。两者都指向一块内存区域.
二、require()加载模块
Node.js依然遵循 CommonJS 规范,使用 require 关键字来加载模块。
而要区分两者,用内存指向的方式更好理解:exports 只是 module.exports的引用,辅助后者用于添加内容。
//test.js
let a = 'Hello Wolrd!';
console.log(module.exports); //能打印出结果为:{}
console.log(exports); //能打印出结果为:{}
exports.a = 'Hello FuZhou!'; //这里 exports 将 module.exports 的内容给改成 {a : 'Hello FuZhou!'}
exports = 'Bye Bye!'; //这里将 exports 的指向改变
//exe.js
const a = require('/test');
console.log(a) // 打印为 {a : 'Hello FuZhou!'}
由此说明,require导出的内容是module.exports指向的内存块内容,并不是exports指向的内存块内容。
三、官方文档中exports的应用
在源码中,经常可以看到以下写法:
exports = module.exports = somethings
上面的代码其实等价于:
module.exports = somethings
exports = module.exports
原理:当 module.exports 指向新对象时,exports 断开了与 module.exports 的引用;所以通过 exports = module.exports 让 exports 重新指向 module.exports 。
四、小结
Node.js 认为每个文件都是一个独立的模块。
如果包有两个文件,假设是“a.js” 和“b.js”。如果“b.js” 需要使用“a.js” 的功能,则“a.js” 必须要通过给 exports 对象增加属性来暴露这些功能:
// a.js
exports.verifyPassword = function(user, password, done) { ... }
完成这步后,所有需要“a.js” 的都会获得一个带有“verifyPassword” 函数属性的对象:
// b.js
require(‘a.js’) // { verifyPassword: function(user, password, done) { ... } }
如果想直接暴露这个函数(a.js),而不是让它作为某些对象的属性,则可以通过覆写 exports 来达到目的:
// a.js
module.exports = function(user, password, done) { ... }
但是需要注意的是,绝对不能把它当做一个全局变量。
总之,“module.exports” 和“exports” 之间的区别经常会使 Node.js 新手踩坑。
但只要记住的是:把“exports” 当做 module 对象的一个属性。