uncategorized

exports 和 module.exports

浅谈 exports 和 module.exports 的区别和使用场景

module.exports 和 exports 的作用

两者都是作为当前导出模块的挂载点而存在, 当我们需要导出一个函数(或对象, 或直接一个变量)来供别的模块 require 引入的时候, 我们可以使用

1
2
3
module.exports.funcName = myFunc(){} 
// 或者
exports.funcName = myFunc(){}

require()的结果是{funcName: [function]}, 可见导出的方法是作为一个属性挂载在整个大的 exports 对象上的
并且当我们为 exports.funcName 赋值的时候, exports 也会出现一个相同的 exports 的属性, 一个对象属性的改变会引起另一个对象的随之改变, 反之亦然, 二者关系见下文
我们也可以导出整个对象而不是作为导出对象的属性来导出

1
2
3
module.exports = myFunc(){}
// 或者
exports = myFunc(){}

不过由于 module.exports 和 exports 的相互关系, 使用这种方法进行导出的时候需要注意, 当我们这样直接为对象赋值的时候, 对其中一个对象赋值不会引起另一个对象的同步改变

二者的关系

exports 本质上是 module.exports 的一个引用, 这就说明了为什么这两个对象的属性会同步改变, 并且当我们以module.exports = myFunc() 或者 exports = myFunc(){} 这样的方式赋值的时候, 另一个对象不会同步改变的原因: 因为原本二者指向同一个对象, 直接对指针赋值,其实是改变了指针的指向,而不是改变指针原本指向的对象上的属性,现在这样赋值就破坏了原本的相同引用的关系

它们从哪里来, 我为什么能直接用?

在一个模块的代码被编译的时候, 用户写的代码会被一个包装函数给包起来:

1
2
3
4
(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
module.exports.funcName = myFunc(){}
});

而包装函数被调用的过程看起来像是这样的(来源官方文档):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function require(/* ... */) {
const module = { exports: {} };
((module, exports) => {
// Module code here. In this example, define a function.
function someFunc() {}
exports = someFunc;
// At this point, exports is no longer a shortcut to module.exports, and
// this module will still export an empty default object.
module.exports = someFunc;
// At this point, the module will now export someFunc, instead of the
// default object.
})(module, module.exports);
return module.exports;
}

由此也可理解 exports 和 module.exports 的关系

为什么要有 exports 呢

既然 exports 是 module.exports 的一个引用, 那么我直接对 module.exports 进行操作不就好了吗, 为什么我还需要 exports 呢?

答案是

  1. 为了方便性
  2. 以及写出更清晰简洁的代码(个人看法, 毕竟代码是写给人看的)

nodejs 官方文档给出的解释是:

It allows a shortcut, so that module.exports.f = … can be written more succinctly as exports.f = …

export for ES6

1
2
3
4
5
6
7
8
9
10
11
12
export * from 'lib1'
// 从lib1中导出所有[命名export]

export { default } from 'lib2'
// 如果当前文件没有default-export, 以上代码会将lib2中的default-export 作为当前文件的default导出
// 如果当前文件已经存在default, 则会报错(不能存在多个default

export { default } from 'lib3'
export { default } from 'lib4'
// 如果当前文件没有default, 但声明多处
// export { default } from ''
// 则取第一个执行的, 上例为lib3的export

◉ End.


参考资料:

  1. modules exports shortcut
  2. modules the module wrapper

如博文有叙述不妥以及不准确的地方, 望各位看客不吝赐教, 感谢.

Share