CommonJs的实现原理
1.在Node中任何js、json文件都是一个模块
2.Node模块化作用域隔离使用的是函数的作用域实现的,每个文件内部的代码会被Node包装到一个函数内部等到被require的时候执行函数,返回模块的导出内容并缓存模块的执行结果,下次require引用直接从缓存中获取,由于require的加载是同步的所以会阻塞代码执行
3.模块化解决的主要问题
1)抽离公共方法,提高代码复用率
2)代码按照模块解耦,作用域之间相互隔离互相不影响,实现高内聚低耦合
3)按需加载提升效率,只有执行require才会运行模块的代码
4.可以通过require('module')获取node中全局的模块构造函数,系统加载的模块会统一放入到这个全局的模块构造函数的静态属性_cache对象中,这样一来只需要加载一次模块,后续如果要使用相同的模块只需要从这个模块构造函数的静态属性_cache中获取即可
const globalModule = require('module')
console.log(globalModule);
5.使用js代码模拟Node读取文件代码内容后进行的包装增强过程
1)main.js
const fs = require("fs");
const path = require("path");
const vm = require("vm");
// 全局的模块类
class MyModule {
constructor(id) {
this.id = id;
this.export = {};
}
// 设置静态属性,相当于设置类自己的属性,生成的对象没有该属性,只能通过类访问
static wrapper = [
"(function(myExports, myModule, MyRequire, __dirname, __filename){",
"})",
];
static execCode(myModule) {
const content = fs.readFileSync(myModule.id, "utf8");
const fnStr = MyModule.wrapper[0] + content + MyModule.wrapper[1];
const fn = vm.runInThisContext(fnStr);
fn.call(myModule.exports, myModule.exports, myModule, myRequire, __filename, __dirname);
}
static _cache = {};
}
const myRequire = (absPathname) => {
absPathname = path.resolve(__dirname, absPathname);
if (MyModule._cache[absPathname]) {
return MyModule._cache[absPathname].exports;
}
const myModule = new MyModule(absPathname);
MyModule._cache[absPathname] = myModule; // 模块的缓存应该在MyModule.execCode(myModule)前面,这样可以防止循环依赖造成的死循环
MyModule.execCode(myModule)
return myModule.export;
};
const exportData = myRequire("./module.js");
console.log(exportData); // 打印结果:{ name: "张三" }
2)module.js
myModule.export = {
name: '张三'
}
6.参考文献
1)前端技术探秘 - Nodejs 的 CommonJS 规范实现原理 | 京东物流技术团队 - 知乎 (zhihu.com)
2)JavaScript模块化开发的前世今生 - 果冻想 - SegmentFault 思否
3.CommonJS 和 ES6 Module 究竟有什么区别? - 掘金 (juejin.cn)
文件模块
1.node文件模块简介: