CommonJS 与循环依赖
CommonJS 的加载机制是什么?
- 场景: 主要用于 Node.js, 本地文件加载远快于 ESM;
- 同步: 构建、实例化与求值一次性同步完成;
- 遍历: 深度优先后序;
- 缓存: 在查找下一个模块之前先执行当前模块代码 (直到
require语句) 并实时缓存; - 缓存命中: 再次
require同一模块时直接读取缓存, 不再执行;
导出变量为什么是值拷贝?
- 机制: 导入方拿到的是导出变量在导入时刻的副本;
- 结果: 导出方之后修改变量, 导入方读到的仍是旧值;
- 对比: ESM 的 live binding 指向同一内存地址, 见 ESM 加载机制;
循环依赖时输出顺序如何推演?
// index.js
var a = require("./a");
console.log("入口模块引用a模块: ", a);
// a.js
exports.a = "原始值-a模块内变量";
var b = require("./b");
console.log("a模块引用b模块: ", b);
exports.a = "修改值-a模块内变量";
// b.js
exports.b = "原始值-b模块内变量";
var a = require("./a");
console.log("b模块引用a模块", a);
exports.b = "修改值-b模块内变量";
- 输出:
b模块引用a模块 { a: '原始值-a模块内变量' }
a模块引用b模块: { b: '修改值-b模块内变量' }
入口模块引用a模块: { a: '修改值-a模块内变量' }
- 推演:
- 执行
index.js并缓存, 加载a.js并缓存; a.js先把原始值-a模块内变量写入exports.a, 再加载b.js;b.js缓存自身后require("./a"), 命中的是已缓存的a.js, 此时a仍是原始值, 因此是值拷贝的旧值;b.js把b改写为修改值后返回a.js,a.js读到修改值;a.js把a改写为修改值后返回入口, 入口读到修改值;
- 执行