weakset 仅支持对象类型,不能标记原始值;用于防对象循环引用时,需先 has() 判断再 add(),原始值因不可变且无引用关系,无需防重复。

WeakSet 不能用于标记原始对象(如字符串、数字、布尔值、null、undefined、symbol),因为它只接受对象类型作为成员。传入原始值会直接抛出 TypeError: Invalid value used in weak set。
所以,“用 WeakSet 标记已处理的原始对象”这个前提本身不成立——它在语法和设计上就被禁止。
如果你的目标是防止递归中因循环引用导致的无限死循环,那 WeakSet 确实有用,但仅适用于对象类型(包括普通对象、数组、Map、Set、Date、RegExp、DOM 节点等),前提是:
- 你手动控制递归流程
- 在每次进入新对象前,先用
weakSet.has(obj)检查 - 确认未处理过,才调用
weakSet.add(obj),再继续递归
比如深克隆时防循环:
-
正确做法:
-
if (typeof obj === 'object' && obj !== null)才进入 WeakSet 判断逻辑 - 对原始值(string/number/boolean/null/undefined)直接返回,不走 WeakSet 流程
- 数组、普通对象、Map/Set 实例等对象类型,才用 WeakSet 记录
-
-
常见错误:
- 把
seen.add(123)或seen.add('hello')写进代码 → 立即报错 - 忘了在
add()前做has()判断 → 刚加进去就递归进去,等于没防 - 对非对象类型也强行塞进 WeakSet,误以为能“统一处理所有值”
- 把
那原始值怎么防重复?其实不需要防。
- 原始值是按值比较、不可变、无引用关系
- 递归中遇到相同数字或字符串,不会引发循环引用,也不会导致栈溢出
- 真正危险的是对象之间的相互引用:
a.b = b; b.a = a这类结构
总结一句话:
WeakSet 是为对象而生的“轻量登记簿”,它不支持原始值,也不需要支持——因为原始值天生就不会造成循环引用问题。你要防的不是“重复的 42”,而是“绕不开的 objA → objB → objA”。










