JavaScript Set对象是什么_它如何存储唯一值

JavaScript Set 使用 SameValueZero 算法判断重复,NaN 和 ±0 被视为相同,对象按引用比较,无键概念,仅存值,不支持下标访问,去重对象数组需手动处理唯一标识。

JavaScript Set 是一个内置构造函数,用来创建元素唯一、无序的集合。它不基于键值对,也不允许重复值——哪怕两个值在 JavaScript 中“相等”,Set 也会按 SameValueZero 算法判断是否重复,这意味着 NaNNaN 被视为相同,而 +0-0 也被视为相同(这点和 Object 的键不同)。

Set 如何判断值是否重复

Set 内部使用 SameValueZero 比较算法,不是 ===,也不是 Object.is()(虽然非常接近)。关键行为包括:

  • NaN 只会被存一次:
    const s = new Set([NaN, NaN]); s.size // 1
  • +0-0 被视为同一个值:
    const s = new Set([+0, -0]); s.size // 1
  • 对象即使内容相同,也视为不同(因为引用不同):
    const a = {x: 1}; const b = {x: 1}; new Set([a, b]).size // 2
  • 原始值与对应包装对象被视为不同:
    new Set([1, new Number(1)]).size // 2

Set 存储的是值本身,不是键名

Map 或普通对象不同,Set 没有“键”概念。它只维护一个值列表,每个值既是“键”也是“值”。这带来几个实际影响:

  • 不能用 set[key] 访问,必须用 set.has(value) 查询存在性
  • 没有类似 Object.keys() 的原生键枚举方法;遍历靠 for...ofset.values() 或展开运算符 [...set]
  • 添加重复值不会报错,也不会覆盖,只是静默忽略:
    const s = new Set(); s.add(42); s.add(42); s.size // 1

常见误用:想用 Set 去重对象数组?得自己处理

直接把对象放进 Set 不会按属性去重,因为对象比较看引用。如果要按结构去重,必须先序列化或提取唯一标识:

  • 错误写法(无效去重):
    const arr = [{id: 1}, {id: 1}]; const unique = [...new Set(arr)]; // length === 2
  • 正确思路之一(用 ID 字符串做中介):
    const arr = [{id: 1}, {id: 1}, {id: 2}]; const seen = new Set(); const unique = arr.filter(item => { if (seen.has(item.id)) return false; seen.add(item.id); return true; });
  • 注意:JSON.stringify() 不可靠(属性顺序、undefined、function、Symbol 会被忽略或报错)

真正容易被忽略的是:Set 的“唯一性”完全依赖运行时的值表示,不是开发者语义上的“相同”。一旦你混用原始值和包装对象、或依赖对象结构而非引用,就很容易以为去重了,其实没生效。