require 的运行机制和缓存策略你了解吗?javascript
require 加载模块的是同步仍是异步?谈谈你的理解java
exports 和 module.exports 的区别是什么?node
require 加载模块的时候加载的到底是什么?git
做者简介:koala,专一完整的 Node.js 技术栈分享,从 JavaScript 到 Node.js,再到后端数据库,祝您成为优秀的高级 Node.js 工程师。【程序员成长指北】做者,Github 博客开源项目 github.com/koala-codin…程序员
提到 exports 和 module.exports 咱们不得不提到 require 关键字。你们都知道 Node.js 遵循 CommonJS 规范,使用 require 关键字来加载模块。github
问题:不知道小伙伴们在使用 require 引入模块的时候有没有相关,多个代码文件中屡次引入相同的模块会不会形成重复呢?面试
由于在 C++ 中一般使用#IFNDEF等关键字来避免文件的重复引入,可是在 Node.js 中无需关心这一点,由于 Node.js 默认先从缓存中加载模块,一个模块被加载一次以后,就会在缓存中维持一个副本,若是遇到重复加载的模块会直接提取缓存中的副本,也就是说在任什么时候候每一个模块都只在缓存中有一个实例。数据库
先回答问题,同步的! 可是面试官要是问你为何是同步仍是异步的呢? 其实这个答案并非固定的,可是小伙伴们能够经过这几方面给面试官解释。后端
Node.js 会自动缓存通过 require 引入的文件,使得下次再引入不须要通过文件系统而是直接从缓存中读取。不过这种缓存方式是通过文件路径定位的,即便两个彻底相同的文件,可是位于不一样的路径下,会在缓存中维持两份。 能够经过缓存
console.log(require.cache)
复制代码
获取目前在缓存中的全部文件。
在一个 node 执行一个文件时,会给这个文件内生成一个 exports 和 module 对象, 而module又有一个 exports 属性。他们之间的关系以下图,都指向一块{}内存区域。
exports = module.exports = {};
复制代码
看一张图理解这里更清楚:
require()加载模块的时候咱们来看一段实例代码
//koala.js
let a = '程序员成长指北';
console.log(module.exports); //能打印出结果为:{}
console.log(exports); //能打印出结果为:{}
exports.a = '程序员成长指北哦哦'; //这里辛苦劳做帮 module.exports 的内容给改为 {a : '程序员成长指北哦哦'}
exports = '指向其余内存区'; //这里把exports的指向指走
//test.js
const a = require('/koala');
console.log(a) // 打印为 {a : '程序员成长指北哦哦'}
复制代码
看上面代码的打印结果,应该能获得这样的结论:
require导出的内容是module.exports的指向的内存块内容,并非exports的。 简而言之,区分他们之间的区别就是 exports 只是 module.exports的引用,辅助后者添加内容用的。用内存指向的方式更好理解。
看一下官方文档中exports的应用
exports = module.exports = somethings
复制代码
上面的代码等价于:
module.exports = somethings
exports = module.exports
复制代码
原理很简单,即 module.exports 指向新的对象时,exports 断开了与 module.exports 的引用,那么经过 exports = module.exports 让 exports 从新指向 module.exports 便可。
建议:在使用的时候更建议你们使用module.exports(根据下面的例子也能得出)
Node.js 认为每一个文件都是一个独立的模块。若是你的包有两个文件,假设是“a.js” 和“b.js”,而后“b.js” 要使用“a.js” 的功能,“a.js” 必需要经过给 exports 对象增长属性来暴露这些功能:
// a.js
exports.verifyPassword = function(user, password, done) { ... }
复制代码
完成这步后,全部须要“a.js” 的都会得到一个带有“verifyPassword” 函数属性的对象:
// b.js
require(‘a.js’) // { verifyPassword: function(user, password, done) { ... } }
复制代码
然而,若是咱们想直接暴露这个函数,而不是让它做为某些对象的属性呢?咱们能够覆写 exports 来达到目的,可是咱们绝对不能把它当作一个全局变量:
// a.js
module.exports = function(user, password, done) { ... }
复制代码
注意到咱们是把“exports” 当作 module 对象的一个属性。“module.exports” 和“exports” 这之间区别是很重要的,并且常常会使 Node.js 新手踩坑。
加入咱们一块儿学习吧!
交流群满100人不能自动进群, 请添加群助手微信号:【coder_qi】备注node,自动拉你入群。