0
点赞
收藏
分享

微信扫一扫

module.exports属性与exports变量的区别

书呆鱼 2021-09-25 阅读 74
前端

一、CommonJS 模块规范

Node 应用由模块组成,采用 CommonJS 模块规范。
每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。

CommonJS 规范规定,每个模块内部,module 变量代表当前模块。这个变量是一个对象,它的 exports 属性(即 module.exports )是对外的接口。加载某个模块,其实是加载该模块的 module.exports 属性。

CommonJS 规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。

1. module.exports 属性

module.exports 属性表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取 module.exports 变量。

2. exports 变量

为了方便,Node 为每个模块提供一个 exports 变量,它执行 module.exports。这等同于在每个模块头部,有一行这样的命令:var exports = module.exports,我们可以通过以下方式来验证

console.log(exports === module.exports);  // true

所以在对外输出模块接口时,可以向 exports 对象添加属性方法。

module.exports.age = 20
module.exports.getAge = function() {}

// 相当于
exports.age = 20
exports.getAge = function() {}

但是不能直接将 exports 变量指向一个值,因为这样等于切断了 exports 与 module.exports 的联系。

// 以下写法无效,因为 exports 不再指向 module.exports 了。
exports = function() {};

3. module.exports 与 exports 的使用

当一个模块的对外接口,只是一个单一的值时,不能使用 exports 输出,只能使用 module.exports 输出。

// moduleA.js
// 1️⃣ 正确 ✅
module.exports = function() {};

// 2️⃣ 错误 ❎
exports = function() {};

导入模块看结果:

// other.js
var moduleA = require('moduleA.js');
console.log(moduleA);

// 两种写法打印的值分别为:
// 1️⃣ 预期结果 ✅  ƒ () { console.log('moduleD'); }
// 2️⃣ 非预期结果 ❎  {}

分析结果:
首先我们要知道 module.exports 的初始值是 {},当执行 exports = function() {}; 赋值时,无论赋值的是基本数据类型还是引用数据类型,都将改变 exports 的指向,即切断了 exports 与 module.exports 的联系。但是我们模块对外输出的接口是 module.exports,所以 2️⃣ 得到的是初始值 {}

如果你觉得 exports 与 module.exports 之间的区别很难分清,一个简单的处理方法,就是放弃使用 exports,只使用 module.exports。

* 我个人也没觉得 exports 的写法有多方便,哈哈。

4. 总结

非常简单,三点:

  • module.exports 初始值为一个空对象 {}
  • exports 是指向的 module.exports 的引用
  • require() 返回的是 module.exports 而不是 exports

还是那句话,如果你觉得 exports 与 module.exports 之间的区别很难分清,一个简单的处理方法,就是放弃使用 exports,只使用 module.exports。

二、require() 扩展话题

以下案例源自知乎某帖回答,这里

关于 require() 的解释

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;
}

注意实现顺序,也就是下面代码为什么不成功的原因。

// moduleA.js
module.exports = function() {};
// 为什么这段配置不成功?你们有 BUG!!!
exports.abc = 'abc';

require() 的时候,是先通过 exports.abc 获取, 然后通过 module.exports 直接覆盖了原有的 exports,所以 exports.abc = 'abc' 就无效了。
一般库的封装都是 exports=module.exports= _; (underscore 的例子)。
原因很简单,通过 exports = module.exports 让 exports 重新指向 module.exports 。

三、参考

  1. 阮一峰老师的 CommonJS 规范教程
  2. 某乎某帖

全文终,The end.

举报

相关推荐

0 条评论