weakmap 是为安全绑定 ast 节点编译期元数据而设计的:其弱引用键确保节点被 gc 时元数据自动清理,避免污染原型、内存泄漏及插件冲突。

WeakMap 是为这类场景量身定制的:在不干扰 AST 节点原始结构、不引发内存泄漏的前提下,安全地绑定编译期元数据。
为什么必须用 WeakMap,而不是普通 Map 或属性挂载
AST 节点(如 Babel 的 Node、ESLint 的 ESTree.Node)通常由解析器反复创建、复用甚至丢弃。若用普通对象属性(如 node.__meta = {...}):
- 污染节点原型或实例,可能与工具链其他插件冲突;
- 无法保证节点被 GC 时元数据同步清理,造成隐性内存泄漏;
- 不同插件若都挂同名属性,易覆盖或误读。
而 WeakMap 的键是弱引用 —— 只要 AST 节点对象本身被 JavaScript 引擎回收,对应条目自动消失,无需手动清理。
典型使用模式:在插件生命周期中安全注入与读取元数据
以 Babel 插件为例,推荐在
pre钩子中初始化 WeakMap,在遍历中写入,在post中读取或转换:
deep-java-review下载Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
const nodeMeta = new WeakMap(); module.exports = function (babel) { return { pre(file) { // 每次编译新文件时新建 WeakMap,隔离作用域 file.set('nodeMeta', new WeakMap()); }, visitor: { Identifier(path) { const meta = { isUsed: true, scopeDepth: path.scope.depth }; // 安全附加到当前节点 file.get('nodeMeta').set(path.node, meta); }, FunctionDeclaration(path) { const meta = { isExported: path.parentPath.isExportDeclaration() }; file.get('nodeMeta').set(path.node, meta); } }, post(file) { const metaMap = file.get('nodeMeta'); // 后续可基于元数据做优化,例如标记未使用的变量 path.traverse({ Identifier(path) { const data = metaMap.get(path.node); if (data && !data.isUsed) { path.remove(); } } }); } }; };跨插件协作:通过统一 Symbol 或上下文桥接 WeakMap
多个插件需共享元数据时,避免各自创建 WeakMap。推荐方式:
- 定义一个私有 Symbol 作为全局键(如
const AST_META = Symbol('ast-meta')),在file.metadata或自定义上下文对象中存 WeakMap 实例; - 插件间约定 key 名(如
'babel-plugin-minify:sideEffect')避免字段冲突; - 不直接暴露 WeakMap,而是封装 getter/setter 工具函数,增强类型提示与访问控制。
例如:
const AST_META = Symbol('ast-meta'); function getMeta(node) { const map = node[AST_META] || new WeakMap(); node[AST_META] = map; return map; } // 在插件中调用 getMeta(path.node).set('my-plugin:transformed', true);注意事项与常见陷阱
WeakMap 不支持遍历、
size属性或clear()方法,设计上就是“只写只读、不可观测”。这意味着:- 无法在调试时枚举所有已标记节点 —— 应配合日志或开发模式专用辅助 Map 做临时快照;
- 不能用
node === otherNode判断是否同一节点再查 WeakMap —— 必须确保传入的是原始引用; - Babel 的
path.node和 ESLint 的context.getSourceCode().ast节点不是同一个对象,跨工具链不能共用 WeakMap。若需持久化或跨阶段传递,应改用
path.state(Babel)、context.options(ESLint)或显式构建中间 IR 结构。
- 定义一个私有 Symbol 作为全局键(如
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










