最近看Node.js的时候,看到有exportsmodule.exports两种导出模块的方式,十分好奇它们有什么区别,于是研究了一番

Node里面的模块系统遵循的是CommonJS规范。
那问题又来了,什么是CommonJS规范呢?

CommonJS定义的模块分为: 模块标识(module)、模块定义(exports) 、模块引用(require)

moduleexports都是Node.js中的内置对象,我们在控制台把它们输出来

console.log(exports)
// {}

console.log(module)
//Module {        
//  id: '.',
//  path: 'F:\\node\\test',
//  exports: {},
//  ......
//}
可以看出来,exportsmodule中的一个属性,并且都是空对象{},实际上exports是module.exports的一个引用,exports指向的是module.exports,它们指向同一个内存地址,也就是说不改变它们内存指向的情况下,它们是等价的,即module.exports === exports

内存结构示意图


现在新建一个a.js用来导出模块,新建一个b.js用来导入模块
使用module.exports的方式导出:

//a.js
module.exports.name = 'amy'

//b.js
var a = require('./b.js')
console.log(a)  // 输出: { name: 'amy' }

使用exports属性的方式导出:

//a.js
exports.name = 'amy'

//b.js
var a = require('./b.js')
console.log(a)  // 输出: { name: 'amy' }

使用exports直接赋值的方式导出:

//a.js
exports = { name: 'amy' }

//b.js
var a = require('./b.js')
console.log(a)  // 输出: {}
从输出结果可以看出,使用module.exports和exports属性两种导出方式是一样的,直接给exports赋值的导出方式是无效的

我们再看《Node.js开发指南》中第三章Node.js 快速入门的一段话

在外部引用该模块时,其接口对象就是要输出的对象本身,而不是原先的exports。

事实上,exports 本身仅仅是一个普通的空对象,即 {},它专门用来声明接口,本
质上是通过它为模块闭包①的内部建立了一个有限的访问接口。因为它没有任何特殊的地方,
所以可以用其他东西来代替。

不可以通过对 exports 直接赋值代替对 module.exports 赋值。exports 实际上只是一个和 module.exports 指向同一个对象的变量,它本身会在模块执行结束后释放,但 module 不会,因此只能通过指定module.exports 来改变访问接口。


总结

require引入的对象本质上是module.exports,虽然使用exports点属性的方式也可以导出,但是为了避免出错,尽量都用 module.exports 导出,然后用require导入。


置酒高殿上
24 声望0 粉丝