Node中的exports和module.exports的区别

Node中,采用CommonJS模块规范。 什么是CommonJS规范

CommonJS 规范是为了解决 JavaScript 的作用域问题而定义的模块形式,可以使每个模块它自身的命名空间中执行。该规范的主要内容是,模块必须通过 module.exports 导出对外的变量或接口,通过 require() 来导入其他模块的输出到当前模块作用域中。

简单的说就是,根据这个规范,每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。

那在Node中导入模块毫无疑问就是以require的形式,导出模块有exportsmodule.exports两种,本文主要讲的就是这两者的区别。

一句话概括:require返回的其实module.exports这个对象,并不是exports,而我们在编写模块时用到的exports对象实际上只是对module.exports的引用(reference)。

那这里涉及一个引用概念:
ECMAScript的数据类型算上es6的Symbol有7种,这7种又可以分为以下两种。

  1. 基本类型:UndefinedNullBooleanNumberStringSymbol
  2. 引用类型: Object

举个引用类型的例子:

1
2
3
4
var a = { name: 123 }
var b = a // 此处赋值其实是把b指向a在内存中的地址
b.name = 456
console.log(a) // { name: 456 } 引用导致a也变了

那么在Node的每一个文件模块当中,module.exports默认值为{}, exports当然也指向这个空对象。所以下面两者效果其实是一样的:

1
2
3
4
5
6
7
8
// a.js
function foo(){
console.log('foo')
}
exports.foo = foo
module.exports.foo = foo // 两者都是一样的

那么在引用的时候:

1
2
3
// b.js
var a = require('./a')
a.foo() // foo

但是不能这么写:

1
2
3
4
5
6
7
// a.js
function foo(){
console.log('foo')
}
exports = foo // ✘ 这是错误的,因为这里直接把exports指向了另一个地址,导致与`module.exports`两者引用关系断裂,require返回的其实是module.exports的初始值{},拿不到foo。

引用时:

1
2
3
4
// b.js
var a = require('./a')
a() // TypeError: a is not a function

这样写才是ok的:

1
2
3
4
5
6
// a.js
function foo(){
console.log('foo')
}
module.exports = foo // ✅ 这种是ok的。

引用时:

1
2
3
4
// b.js
var a = require('./a')
a() // foo 是能正常输出的

至此,exportsmodule.exports的区别就是以上内容。

菜鸟学习笔记,如有不对,还希望高手指点。如有造成误解,还希望多多谅解。

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。