NaN 在 Set 和 Map 中被视为与自身相等,因其内部使用 SameValueZero 算法(+0 与 -0 相等、NaN 与 NaN 相等),而非 ===;故 new Set([NaN, NaN]) 长度为 1,Map 可正确 set/get NaN 键,但数组方法或对象字面量无法模拟此行为。

NaN 在 Set 和 Map 中被当作相等值处理,这是 JavaScript 的明确规范行为,和 NaN !== NaN 的表层直觉相反。关键在于:Set 和 Map 的内部比较逻辑不是简单用 ===,而是在其“唯一性判定”中对 NaN 做了特殊约定——NaN 被视为与自身相等。
NaN 在 Set/Map 中的去重与查找机制
Set 判断元素是否已存在、Map 判断键是否已存在,都基于一种叫 “SameValueZero” 的抽象比较算法。它和 === 几乎一致,唯一例外就是:+0 与 -0 视为相等,NaN 与 NaN 也视为相等。
-
new Set([NaN, NaN])最终只含一个 NaN -
const m = new Map(); m.set(NaN, 'a'); m.get(NaN)正确返回'a',哪怕NaN === NaN是false -
0和-0同样被归为同一个键或同一个值(0 === -0为true,所以这点其实和===一致)
为什么不能靠 === 来模拟这个行为
如果你手动写去重逻辑,比如用 arr.filter((v, i) => arr.indexOf(v) === i),那 NaN 就不会被去重——因为 indexOf 内部用的是严格相等(===),而 NaN === NaN 永远是 false。但 Set 不依赖 ===,它用的是语言底层的 SameValueZero。
- 这意味着你无法用普通数组 + includes/filter 模拟 Set 对 NaN 的行为
- 也不能用对象字面量(
{[NaN]: 1})来存 NaN 键——它会被自动转成字符串"NaN",完全失去原始语义
实际识别技巧:用 has() 和 size 验证
最直接的方式是动手验证,而不是凭印象判断:
-
const s = new Set([NaN]); s.add(NaN); console.log(s.size)→ 输出1,说明第二次添加未生效 -
const m = new Map(); m.set(NaN, 'x'); console.log(m.has(NaN))→ 输出true -
const s2 = new Set([0, -0, NaN, NaN]); console.log(s2.size)→ 输出2(0 和 NaN 各一个)
注意包装对象带来的干扰
NaN 是原始值,但 new Number(NaN) 是对象,它不参与 SameValueZero 特殊规则:
-
new Set([NaN, new Number(NaN)])包含两个成员(原始 NaN ≠ 包装对象) -
NaN === new Number(NaN)是false,且Object.is(NaN, new Number(NaN))也是false - 只有原始值 NaN 才享受“自等”待遇;所有包装对象都按引用或常规
===判定

















