set对对象只比较内存地址而非属性值,因其内部使用samevaluezero算法(与===一致),不调用tostring()或valueof(),也不支持深度比较或自定义相等逻辑。

JavaScript 的 Set 对对象不做“深度比较”,它只认内存地址——两个 {a: 1} 字面量哪怕结构一模一样,也是两个独立对象,分配在不同内存位置,=== 判断为 false,Set 就认为它们不重复。
根本原因:Set 依赖 SameValueZero 算法
Set 内部用的是 SameValueZero 比较规则(与 === 基本一致,仅对 +0/-0 和 NaN 特殊处理)。该规则对对象只比较引用,不遍历属性、不递归检查嵌套值。所以:
-
new Set([{x: 1}, {x: 1}])包含两个元素 -
new Set([{}, {}])也包含两个元素 - 哪怕对象只有 1 层、1 个属性、全是基本类型,只要不是同一引用,就无法去重
为什么不能靠 toString() 或 valueOf() 干预?
Set 完全不调用对象的 toString()、valueOf() 或任何自定义方法。这些方法在对象转字符串或数值运算时才起作用,而 Set.has() 或 add() 过程中不会触发它们——这是常见误解来源。
所谓“深度限制”其实是根本没深度
“深度”在这里是误导性说法。Set 并非“限制在某一层”,而是压根没有设计深度逻辑。它不像 Java 的 HashSet 那样允许重写 hashCode() 和 equals(),也不像某些库提供比较器参数。它的行为是固定的、底层的、不可覆盖的。
验证方式很简单
只需两行代码就能确认:
const s = new Set(); s.add({a: 1}); console.log(s.has({a: 1})); // falseconsole.log({a: 1} === {a: 1}); // false
第二行结果直接解释了第一行——has() 的判断依据就是 ===。










