weakmap 无法转换为 json,因其键不可枚举、不可遍历、无 size 属性,json.stringify 返回空对象;这是 ecmascript 规范刻意设计的弱引用安全机制,非实现缺陷。

WeakMap 本身不支持“从外部读取并克隆原始键值对”,这不是一个需要识别的“悖论”,而是它设计上的根本限制——WeakMap 的键不可枚举、不可遍历、不可获取。所谓“拷贝的悖论”,其实是误把 WeakMap 当作普通 Map 或缓存容器来使用,忽略了它的底层契约。
WeakMap 的键天然不可见
WeakMap 的核心特性之一是:它不提供任何遍历或查询接口(如 keys()、entries()、has() 以外的检查方式)。你无法列出它存了哪些对象,也无法取出所有键或键值对。
-
weakMap.keys()→ 报错:方法不存在 -
weakMap.forEach(...)→ 报错:方法不存在 -
Array.from(weakMap)→ 报错:WeakMap 不可迭代 - 唯一可用的检查方式只有
weakMap.has(obj)和weakMap.get(obj),且都依赖你**已经持有那个原始对象引用**
深拷贝函数里用 WeakMap,不是为了“导出映射”,而是为了“内部防重入”
在 cloneDeep 实现中,WeakMap 是作为私有缓存使用的,作用非常具体:
- 当递归进入某个对象
obj时,先查cache.has(obj) - 若存在,直接返回
cache.get(obj)(避免循环引用导致栈溢出) - 若不存在,新建副本,再执行
cache.set(obj, cloned) - 整个过程完全不依赖“读取全部键值对”,也不需要对外暴露任何缓存内容
为什么不能用 WeakMap 做可导出的拷贝映射?
因为 WeakMap 的弱引用机制与“可导出性”本质冲突:
- 如果允许外部遍历键,就等于强制持有了这些对象的强引用,违背“弱引用”初衷
- 一旦你尝试保存键的副本(比如 push 到数组),该对象就不再“弱”,GC 就无法及时回收
- 规范明确禁止暴露键集合——这是语言层的设计约束,不是实现缺陷
想记录/导出拷贝关系?换用其他结构
如果你确实需要事后查看“哪个原对象对应哪个克隆体”,WeakMap 就不合适。可考虑:
-
Map:支持完整遍历,但需自行管理生命周期,防止内存泄漏 - 带 ID 标识的对象池:给每个原始对象打唯一标记(如
obj.$$id = Symbol()),再用普通对象{ [id]: cloned }映射 - 调试专用模式:仅开发环境启用 Map 缓存,生产环境切回 WeakMap
WeakMap 不是“隐藏了数据”,而是根本没提供访问通道——这不是 bug,是 feature。深拷贝用它,图的是安全和轻量,而不是可审计性。











