Set和Map数据结构

26 阅读5分钟

Set 数据结构

定义Set 是一种类似数组的数据结构,但其成员的值都是唯一的(自动去重)。

创建与传参:作为构造函数,它可以接收任何具有 iterable 接口的数据结构(如数组、字符串)作为参数来初始化。

javascript

new Set([1, 2, 3]); // 来自数组
new Set('abc'); // 来自字符串

实例属性和方法

属性/方法说明与示例返回值
set.size返回Set实例的成员总数。Number
set.add(value)添加某个值,返回Set本身(支持链式调用)。Set 对象
set.has(value)判断该值是否为Set的成员。Boolean
set.delete(value)删除某个值。Boolean(表示是否删除成功)
set.clear()清除所有成员。undefined

Set 与数组的相互转换

  1. Set => 数组(最常用的去重方法):

    javascript

    // 方法1:扩展运算符
    let arr1 = [...new Set([1, 2, 2, 4])]; // [1, 2, 4]
    // 方法2:Array.from
    let arr2 = Array.from(new Set([1, 2, 4]));
    
  2. 数组 => Set

    javascript

    new Set([1, 2, 3]); // 自动去重
    

遍历方法

Set 结构的键名和键值是同一个值,因此 keys() 和 values() 的行为完全一致。

  • Set.prototype.keys():返回键名的遍历器。
  • Set.prototype.values():返回键值的遍历器。
  • Set.prototype.entries():返回键值对的遍历器(每项为 [value, value])。
  • Set.prototype.forEach():使用回调函数遍历每个成员。回调函数参数为 (value, value, set)

WeakSet

定义与特点

  • 成员只能是对象h或者Symblo值,不能是其他类型的值。
  • 对成员对象是弱引用。这意味着,如果没有其他变量引用该对象,即使它存在于 WeakSet 中,垃圾回收机制也会自动回收其占用的内存。
  • 由于成员可能随时被回收,WeakSet 没有 size 属性,也无法被遍历

创建与传参
构造函数接受的参数必须是一个可迭代对象,且其每个成员都是对象。通常使用二维数组,因为内层数组的成员才是 WeakSet 的值

javascript

// 正确:二维数组,成员[1,2][3,4]是对象(数组是引用类型)
new WeakSet([[1, 2], [3, 4]]);
// 错误:一维数组成员不是对象
// new WeakSet([1, 2, 3]);

弱引用示例

javascript

let obj = {name: 'qin'};
const ws = new WeakSet([obj]);
obj = null; // 此后,{name: 'qin'} 对象可能被垃圾回收,无法从 ws 中取回。
let obj = {name: 'qin'};
const ws = new Set([obj]);
obj = null; // obj存储解析:obj在栈中定义key:value的形式,key是obj,value是hash值,obj在堆内开辟一个空间地址,用来存储{name:'qin'},hash值指向了堆内存中对应的地址,手动重置obj时,只是重置了obj对空间地址的引用,ws还会保持对空间地址的引用,所以堆内存的空间没有被gc释放。

实例方法

  • WeakSet.prototype.add(object):添加对象成员。
  • WeakSet.prototype.has(object):判断对象是否存在。
  • WeakSet.prototype.delete(object):删除对象成员。

Map 数据结构

定义Map 是一种完善的“键值对”集合数据结构。与传统对象(Object)只能用字符串或 Symbol 作为键不同,Map 的“键”可以是任何类型的值(包括对象、函数)。

创建与传参
构造函数可以接受一个可迭代对象作为参数,该对象的每个成员必须是一个表示键值对的二元数组

javascript

// 正确:传入一个二维数组,每个子数组是[key, value]
new Map([['name', 'qin'], [{id: 1}, 'objectKey']]);

实例属性和方法

属性/方法说明与示例返回值
map.size返回 Map 实例的成员总数。Number
map.set(key, value)设置键值对。返回Map本身,支持链式调用Map 对象
map.get(key)读取指定键对应的值。value 或 undefined
map.has(key)判断是否有指定的键。Boolean
map.delete(key)删除某个键。Boolean
map.clear()清除所有成员。undefined

遍历方法

  • Map.prototype.keys():返回键名的遍历器。
  • Map.prototype.values():返回键值的遍历器。
  • Map.prototype.entries():返回所有成员(键值对)  的遍历器。
  • Map.prototype.forEach():遍历所有成员。回调函数参数为 (value, key, map)

与其他数据结构的转换

  1. Map => 数组(使用扩展运算符最方便):

    javascript

    const myMap = new Map().set('a', 1).set('b', 2);
    console.log([...myMap]); // [ ['a', 1], ['b', 2] ]
    
  2. 对象 => Map(借助 Object.entries):

    javascript

    let obj = {"a": 1, "b": 2};
    let map = new Map(Object.entries(obj));
    
  3. Map => 对象(当Map的键都是字符串时):

    javascript

    function mapToObj(map) {
      let obj = Object.create(null);
      for (let [k, v] of map) {
        obj[k] = v;
      }
      return obj;
    }
    

WeakMap

定义与特点

  • 只接受对象(null除外)  作为键名,不接受其他类型的值。
  • 对键名所指向的对象是弱引用,不影响垃圾回收。键名对象被回收后,对应的键值对会自动从 WeakMap 中移除。
  • 因此,WeakMap 没有 size 属性,也无法被遍历
  • 应用场景:常用于需要将额外数据与对象关联,又不想影响对象本身生命周期的情况(如DOM元素作为键名存储元数据)。

对象的迭代

普通对象 {} 默认是不可迭代的,不能直接用于 for...of 循环。

遍历普通对象的几种方式:

  1. for...in 循环(遍历自身和继承的可枚举属性键名,通常需搭配 hasOwnProperty 过滤):

    javascript

    for (const key in obj) {
      if (obj.hasOwnProperty(key)) {
        console.log(key, obj[key]);
      }
    }
    
  2. Object.keys() + for...of(遍历自身可枚举属性键名):

    javascript

    for (const key of Object.keys(obj)) {
      console.log(key, obj[key]);
    }
    
  3. Object.values() + for...of(遍历自身可枚举属性值):

    javascript

    for (const value of Object.values(obj)) {
      console.log(value);
    }
    
  4. Object.entries() + for...of(遍历自身可枚举属性键值对):

    javascript

    for (const [key, value] of Object.entries(obj)) {
      console.log(key, value);
    }
    

让普通对象可迭代

通过自定义 [Symbol.iterator] 方法,可以使普通对象支持 for...of 循环。

javascript

const obj = {
  a: 1,
  b: 2,
  [Symbol.iterator]: function* () {
    yield* Object.entries(this); // 使用生成器函数委托给 entries
  }
};
for (const [key, value] of obj) { // 现在可以 for...of 了
  console.log(key, value);
}

总结

特性SetMapWeakSetWeakMap
核心特点值唯一的集合键值对集合,键类型任意弱引用对象集合弱引用对象作为键的集合
键/值类型值可以是任何类型键可以是任何类型值必须是对象键必须是对象
可遍历性
size 属性
垃圾回收影响强引用,会阻止成员被回收强引用,会阻止键被回收弱引用,不影响成员被回收弱引用,不影响键对象被回收