Appearance
实现
本节目标:理解“实现”的核心思路,并能把它用于实际开发或面试表达。 好的,我们来做一个深入的模块系统底层实现教程。我会从 实现简易版 CommonJS 的 require 函数开始,然后扩展讲到 ESModule 的核心机制(包括 live binding 和 module graph)。后续还可以模拟一个带缓存和循环引用支持的 loader,甚至基于 AST 实现一个静态分析器。
一、手写简易版 CommonJS 模块加载器 我们先模拟 Node.js 中的 require 实现。 1. 目录结构: myloader/├── main.js├── a.js└── b.js 2. a.js: const b = require('./b');console.log('a.js 中 b =', b); module.exports = { fromA: 'This is A'}; 3. b.js: const a = require('./a');console.log('b.js 中 a =', a); module.exports = { fromB: 'This is B'}; 4. main.js(手写 require): const fs = require('fs');const path = require('path'); // 模块缓存const moduleCache = {}; function myRequire(modulePath) { const absolutePath = path.resolve(__dirname, modulePath + '.js'); // 缓存命中直接返回 if (moduleCache[absolutePath]) { return moduleCache[absolutePath].exports; } // 初始化 module 对象 const module = { exports: {} }; moduleCache[absolutePath] = module; // 先缓存,支持循环引用 // 读取文件内容 const code = fs.readFileSync(absolutePath, 'utf-8'); // 包装成函数并执行 const moduleWrapper = new Function('require', 'module', 'exports', code); moduleWrapper(myRequire, module, module.exports); return module.exports;} // 启动测试const a = myRequire('./a');console.log('main.js 中 a =', a);
运行结果: $ node main.jsb.js 中 a = {}a.js 中 b = { fromB: 'This is B' }main.js 中 a = { fromA: 'This is A' } 说明:
- 模拟的 myRequire 实现了 Node 的 CommonJS 模型;
- 模块缓存 + 循环引用都已支持;
- 每个模块用 Function 包装,导入导出传参实现作用域隔离。
二、扩展目标 我们后续可以扩展为: ✅ 阶段 1:支持 .json 文件加载(像 Node 那样自动转对象)✅ 阶段 2:实现类似 ESModule 的 import/export,基于 AST 做静态分析: 例如支持这种语法: // a.mjsexport const foo = 'foo';export default 'default from a'; // b.mjsimport foo, { foo as namedFoo } from './a.mjs'; 这需要静态分析 → 构建依赖图(Module Graph) → 执行顺序控制。
三、下一步你想学什么? 你可以选下面一个方向继续深入:
- [推荐] 模拟一个支持 import/export 的 ESM 加载器(通过构建依赖图 + 动态执行)
- 支持 .json、.ts、.mjs 类型模块加载
- 自定义打包器原型(把多个模块合成一个 JS 文件)
- 用 AST 工具(如 acorn)解析 import/export,生成模块依赖图
- 用 vm 模块更安全地运行代码,模拟模块沙箱
选一个我就可以接着写“第二章”,完整带你手写一个模块系统。你更感兴趣哪个? > 来自 <https://chatgpt.com/c/6805aef6-e7b4-8004-ab43-9a137f666985>
