Set的零值相等规则是独立于===和Object.is()的规范判定:NaN视为唯一、+0/-0/0合并为一个0、且+0与-0被认定相等(而Object.is(+0,-0)为false)。

Set 的“零值相等”(Same-value-zero)判定逻辑,不是靠 ===,也不是完全等同于 Object.is(),而是一套独立、规范定义的底层比较规则。它直接影响哪些值会被视为重复、从而被去重。识别它,关键在于观察三类典型行为:NaN 的唯一性、+0 与 -0 的合并、以及与常规相等判断的差异。
看 NaN 是否只留一个
这是最直观的识别方式。尽管 NaN !== NaN 恒为 true,Object.is(NaN, NaN) 为 true,但 Set 的行为是硬编码的:只要遇到 NaN,就认为它和已存在的 NaN 相等,后续不再插入。
-
new Set([NaN, NaN, NaN]).size === 1→ 成立 -
[...new Set([NaN, "NaN"])].length === 2→ 成立(因为"NaN"是字符串,类型不同) - 不能用
arr.filter((v, i) => arr.indexOf(v) === i)模拟,因为indexOf用的是===,会把所有 NaN 都当作不相等
试 +0、-0、0 是否共存
零值相等把 +0、-0 和 0 全部视为同一个值。插入任意组合,Set 最终只保留一个“零”,且打印时统一显示为 0,但内部仍可能保留原始符号(取决于引擎实现)。
-
new Set([+0, -0, 0])→Set { 0 }(大小为 1) -
Array.from(new Set([-0, +0]))[0] === -0可能为 true,也可能为 false——这说明 Set 不保证保留哪个零,只保证不重复 - 别用
String(-0)或JSON.stringify(-0)预处理再塞进 Set,它们都输出"0",会掩盖原本的符号差异,导致误判
对比 === 和 Object.is() 的结果差异
Same-value-zero 是 Object.is() 的近似,但有一个明确例外:它认为 +0 和 -0 相等;而 Object.is(+0, -0) 返回 false。这是识别它的关键分水岭。
-
Object.is(+0, -0) === false,但new Set([+0, -0]).size === 1 -
Object.is(NaN, NaN) === true,和 Set 行为一致;但 Set 对 NaN 的处理是规范强制的“首次出现即锚定”,不依赖任何函数调用 - 数组去重时若依赖手写逻辑(如
reduce+includes),默认用===,就会漏掉 NaN 合并、也分不清 ±0,结果和new Set(arr)不一致
注意类型敏感的边界情况
Same-value-zero 只在值本身比较时起作用,它不跨类型自动转换。类型不同,哪怕数值或字符串内容看起来一样,也绝不会被合并。
-
new Set([0, "0", false])→ 大小为 3(数字 0、字符串 "0"、布尔 false) -
new Set([NaN, "NaN"])→ 大小为 2(NaN 是原始值,"NaN" 是字符串) -
new Set([{}, {}])→ 大小为 2(对象引用不同,Same-value-zero 不介入对象比较,仍用 === 语义)

















