set能正确将nan视为唯一值且只存一个,因其内部使用samevaluezero算法(nan与nan相等、+0与-0相等),不同于===;多次add(nan)后size仍为1,而indexof不识别nan但includes可识别。

JavaScript 中的 Set 能正确将 NaN 视为唯一值,且只允许存在一个 NaN —— 这和 === 的行为不同,是 Set 内部使用“SameValueZero”算法判定相等性的结果。
Set 对 NaN 的唯一性处理是特殊的
Set 判定两个值是否重复时,并不使用严格相等(===),而是采用规范定义的 SameValueZero 算法。该算法规定:
-
NaN === NaN返回false,但 SameValueZero(NaN, NaN) 返回 true -
+0和-0被视为相等(+0 === -0是true,SameValueZero 也返回true) - 其他值的行为与
===一致(如1 === 1、"a" === "a")
实际表现:多次添加 NaN 只保留一个
你可以验证这一点:
const s = new Set(); s.add(NaN); s.add(NaN); s.add(NaN); console.log(s.size); // 1 console.log([...s]); // [NaN]
即使反复添加 NaN,Set 内部仍只存一份。这说明它在内部做了专门处理,把所有 NaN 归为“同一个值”。
和 Map、Array.indexOf 的行为对比
这种设计不是 Set 独有,而是和 Map 键的比较逻辑一致:
-
Map也用 SameValueZero,所以map.set(NaN, 'a'); map.set(NaN, 'b')会覆盖而非新增 - 但
Array.prototype.indexOf()使用===,所以[NaN].indexOf(NaN)返回-1 -
Array.prototype.includes()才使用 SameValueZero,因此[NaN].includes(NaN)返回true
注意:JSON.stringify 会丢失 NaN
虽然 Set 能存住 NaN,但序列化时需小心:
-
JSON.stringify(new Set([NaN]))→"{}"(空对象) -
JSON.stringify([...new Set([NaN])])→"[null]"(NaN被转为null) - 若需保留语义,应自定义序列化逻辑,或改用可序列化的占位符(如
"__NaN__")
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











