当前位置: 首页 > news >正文

exports, module.exports和this 同时设置,最终导出的是什么

总有一些似乎没什么用但总是记不住的面试题,比如这道题:

// a.js
exports.c = 3;
module.exports = {a: 1,b: 2,
};
this.m = 5;// b.js
const a = require('./a');
console.log(a);

这个模块导出什么?

如果只看上面代码中的 exportsmodule.exports,毫无疑问module.exports 的导出会生效。大概能猜出最后输出的是{ a: 1, b: 2}。不过,这个 this 又是啥?他们之间是怎么决定使用哪个的?

exports 、 module.exports 和 this 的区别

Node.js 中,exportsmodule.exports 都用于模块化导出,但它们有一些重要区别:

module.exports

module.exports是模块的输出。
使用 module.exports 实际上是在替换模块的导出对象,而不是添加属性到现有的对象。

// a.js
exports.c = 3;
module.exports = {a: 1,b: 2,
};// b.js
const a = require('./a');
console.log(a); // { a: 1, b: 2}

即使换成下面代码,依旧是module.exports生效。

// a.js
module.exports = {a: 1,b: 2,
};
exports.c = 3;

所以在实践中,通常更推荐使用 module.exports 来保持一致性。

exports

exportsmodule.exports的一个引用,在模块加载时,require返回的是module.exports,而不是exports
使用 exports 时,只是在修改 module.exports 指向的对象,而不是替换它。

// a.js
exports.a = 1;
exports.b = 2;
exports.c = 3;// b.js
const a = require('./a');
console.log(a); // { a: 1, b: 2, c: 3 }

也可以说,在不使用 module.exports 的情况下,exports.c = 3 实际上是相当于 module.exports.c = 3

this

this 在模块中指向 module.exports,因此你可以使用 this 来添加属性到模块的导出对象。

把上面的示例修改一下:

// a.js
exports.a = 1;
this.m = 5;// b.js
const a = require('./a');
console.log(a); // { c: 3, m: 5 }

this.m = 5 实际上是在为 module.exports 添加一个属性 m,这相当于 module.exports.m = 5

在最开始的题目中,重赋值 module.exports 之后,this 也指向新的 module.exports。所以输出结果没有m

导入另一个模块时发生了什么

下面伪代码模拟了 Node.js 中 require 函数的实现,以帮助理解模块导入的过程。

function require(modulePath) {//1. 将modulePath转换为绝对路径://2. 判断是否该模块已有缓存 (cache为require的一个属性)// if(require.cache["D:\\路径\\a.js"]){//   return require.cache["D:\\路径\\a.js"].result;// }//3. 读取文件内容//4. 包裹到一个函数中function __temp(module, exports, require, __dirname, __filename) {exports.c = 3;module.exports = {a: 1,b: 2,};this.m = 5;}//6. 创建module对象 (需调用函数)module.exports = {}; // 先创建空对象const exports = module.exports; // 把module.exports赋值给exports// 调用函数时,把module.exports当作this绑定__temp.call(module.exports, module, exports, require, module.path, module.filename);// 最终返回的是module.exportsreturn module.exports;
}require.cache = {};

在步骤 6 中,__temp.call(module.exports, module, exports, require, module.path, module.filename) 这行代码将 module.exports 作为 this 绑定到 __temp 函数中

// a.js
console.log(this === exports); // true
console.log(this === module.exports); // true

this, exports, module.exports 在一开始是一样的,都是同一个对象。

exports.c = 3; 相当于在对象中加入属性 c, 此时 this 也有 c
module.exports = { a: 1, b: 2}; 相当于把 this 指向新的对象,所以 this.m = 5; 没有生效。

输出过程展示:

// a.js
console.log(this === module.exports); //true
console.log(this === exports); //true
console.log(this); // {}
console.log(exports); // {}
console.log(module.exports); // {}
exports.c = 3;
module.exports = {a: 1,b: 2,
};
this.m = 5;console.log(this === module.exports); // false
console.log(this === exports); // true
console.log(this); // { c: 3, m: 5 }
console.log(exports); // { c: 3, m: 5 }
console.log(module.exports); // { a: 1, b: 2 }// 导出改模块,最终输出为 { a: 1, b: 2 }

总结

  • exportsmodule.exports 是用来定义模块的导出内容。exportsmodule.exports 的一个引用,但如果 module.exports 被重新赋值,exports 的引用将被忽略。

  • this 在模块文件的顶层上下文中,指向 module.exports。在模块开始时,this,exportsmodule.exports 是同一个对象的引用。但重新赋值 module.exports 时指向一个新的对象,exportsthis 仍然指向旧对象。

综合来说:

  1. module.exports:最终决定模块导出的内容。在这个示例中,它会被设置为 { a: 1, b: 2 }exports.cthis.m 都不会影响到最终的导出。

  2. exports.c = 3:不会生效,因为 module.exports 已经被重新赋值了。

  3. this.m = 5:也不会影响最终的导出,因为 module.exports 被重新赋值了。


http://www.mrgr.cn/news/20431.html

相关文章:

  • 新手c语言讲解及题目分享(十七)--运算符与表达式专项练习
  • 【2024】JAVA实现响应式编程Reactor具体API文档使用说明
  • B-smooth 数
  • 判断两个yaw角度之差是否超过了90度
  • knime用三种方法提取列中需要的数据实战
  • Android12——Launcher3文件夹布局修改调整
  • MyPrint打印设计器(十)svg篇-多边形
  • PDF到CAD转换:四大实用工具指南!
  • bun一个现代JavaScript运行时
  • 2024 年高教社杯全国大学生数学建模竞赛题目-A 题 “板凳龙” 闹元宵
  • 详解Python输出
  • 【2024数模国赛赛题思路公开】国赛C题思路丨附可运行代码丨无偿自提
  • 前端黑科技:使用 JavaScript 实现网页扫码功能
  • X 射线测厚仪 -开启高精度厚度检测之门
  • vue+ThreeJS:从0 到1 搭建开发环境
  • 多智能体强化学习:citylearn城市建筑能量优化和需求响应
  • iOS——strong和copy的底层实现
  • PHP一站式解决方案高级房产系统小程序源码
  • 2024高教社杯全国大学生数学建模竞赛B题保姆级分析完整思路+代码+数据教学
  • 2024数学建模国赛选题建议及初步思路来啦!