JavaScript 中的 Map 与 Set 类型讲解

39 阅读6分钟

jym,先祝大家圣诞快乐,今天分享的知识是JS中的Map类型和Set类型。

Map 和 Set 是 ES6 新增的集合类型(区别于传统的 Object 和 Array),用于更高效地存储和管理数据,解决了传统数据结构的诸多局限,二者在存储结构、用途上有明确差异。

一、Set 类型:无序不重复的集合

1. 核心定义

Set 是一种 无序、唯一(不允许重复元素) 的集合数据结构,仅存储「值(value)」,不存储键值对,适合用于去重、快速判断元素是否存在等场景。

2. 核心特性

  • 唯一性:自动过滤重复元素,添加重复值时不会报错,但不会生效;
  • 无序性:元素不按插入顺序维护索引(无法通过下标访问,需通过遍历获取);
  • 存储类型:可存储任意类型的值(原始类型 + 引用类型),判断重复时采用「SameValueZero」算法(类似 ===,但 NaN 视为相等);
  • 无键名:仅存值,没有像 Object 那样的键名映射关系。

3. 常用 API

API 方法 / 属性功能说明示例
new Set([iterable])构造函数,创建 Set 实例,可传入可迭代对象(如数组)初始化const set = new Set([1, 2, 2, 3]); // Set(3) {1, 2, 3}
size属性,返回 Set 中元素的数量set.size; // 3
add(value)添加元素,返回 Set 实例(支持链式调用)set.add(4).add(5); // Set(5) {1, 2, 3, 4, 5}
delete(value)删除指定元素,返回布尔值(成功删除为 trueset.delete(2); // true
has(value)判断元素是否存在,返回布尔值set.has(3); // true
clear()清空 Set 中所有元素,无返回值set.clear(); // Set(0) {}
keys()/values()返回迭代器,遍历所有元素(Set 无键,keys() 等同于 values()for (let val of set.values()) { console.log(val); }
entries()返回迭代器,遍历 [value, value] 格式的数组for (let [val, val2] of set.entries()) { console.log(val === val2); // true }
forEach(callback)遍历所有元素,回调函数接收 (value, key, set)(key 与 value 一致)set.forEach((val) => console.log(val));

4. 典型应用场景

  • 数组去重(最常用):

    const arr = [1, 2, 2, 3, 3, 3];
    const uniqueArr = [...new Set(arr)]; // [1, 2, 3]
    
  • 快速判断元素是否存在(比 Array.includes() 效率更高,尤其数据量大时):

    const userIds = new Set([101, 102, 103]);
    console.log(userIds.has(102)); // true(O(1) 时间复杂度,数组是 O(n))
    
  • 存储不重复的标签 / 筛选条件

    const tags = new Set();
    tags.add("前端").add("JavaScript").add("前端"); // 最终仅存 ["前端", "JavaScript"]
    

二、Map 类型:键值对集合(升级版 Object)

1. 核心定义

Map 是一种有序、键值对的集合数据结构,键(key)和值(value)可以是任意类型(解决了 Object 键只能是字符串 / Symbol 的局限),适合用于存储需要保留插入顺序、键类型多样的映射关系。

2. 核心特性

  • 键的多样性:键可以是任意类型(原始类型:字符串、数字、布尔值、nullundefinedSymbol;引用类型:对象、数组等);
  • 有序性:按元素插入顺序存储,遍历顺序与插入顺序一致(Object 在 ES6 前无序,后续虽保留插入顺序,但有例外);
  • 唯一性:键具有唯一性,重复设置同一键会覆盖原有值(判断键重复的规则同 Set,采用「SameValueZero」算法);
  • 可迭代性:直接支持迭代器遍历,无需像 Object 那样先获取键数组再遍历。

3. 常用 API

API 方法 / 属性功能说明示例
new Map([iterable])构造函数,创建 Map 实例,可传入二维数组(键值对)初始化const map = new Map([["name", "张三"], ["age", 25]]);
size属性,返回 Map 中键值对的数量map.size; // 2
set(key, value)设置键值对,返回 Map 实例(支持链式调用)map.set("gender", "男").set("city", "北京");
get(key)获取指定键对应的值,不存在则返回 undefinedmap.get("name"); // "张三"
delete(key)删除指定键值对,返回布尔值(成功删除为 truemap.delete("age"); // true
has(key)判断键是否存在,返回布尔值map.has("city"); // true
clear()清空 Map 中所有键值对,无返回值map.clear(); // Map(0) {}
keys()返回迭代器,遍历所有键for (let key of map.keys()) { console.log(key); }
values()返回迭代器,遍历所有值for (let val of map.values()) { console.log(val); }
entries()返回迭代器,遍历 [key, value] 格式的数组for (let [key, val] of map.entries()) { console.log(key, val); }
forEach(callback)遍历所有键值对,回调函数接收 (value, key, map)map.forEach((val, key) => console.log(key, val));

4. 典型应用场景

  • 存储多样键类型的映射关系(如以对象为键):

    const user1 = { id: 101 };
    const user2 = { id: 102 };
    const userInfoMap = new Map();
    // 以对象为键,存储用户对应的信息
    userInfoMap.set(user1, { name: "张三", age: 25 });
    userInfoMap.set(user2, { name: "李四", age: 30 });
    console.log(userInfoMap.get(user1)); // { name: "张三", age: 25 }
    
  • 保留插入顺序的键值对存储

    // Object 无法保证顺序(尤其数字键会优先排序),Map 可精准保留插入顺序
    const map = new Map();
    map.set(2, "b").set(1, "a").set("3", "c");
    [...map.keys()]; // [2, 1, "3"](按插入顺序)
    
  • 替代 Object 存储大量动态键值对(便于增删查和遍历,比 Object 更高效):

    // 接口返回的动态筛选条件,用 Map 存储更灵活
    const filterMap = new Map();
    filterMap.set("status", ["active", "pending"])
              .set("date", ["2025-01-01", "2025-12-31"])
              .set("type", "web");
    // 快速遍历筛选条件
    filterMap.forEach((val, key) => {
      console.log(`筛选条件 ${key}:`, val);
    });
    

三、Map vs Set vs Object vs Array 核心对比

数据结构存储形式核心特点适用场景
Set仅存值(无键)无序、不重复数组去重、元素存在性判断、存储唯一标签
Map键值对(键任意类型)有序、键唯一、可迭代多样键映射、保留插入顺序的键值对、动态增删遍历
Object键值对(键仅字符串 / Symbol)无序(ES6 后部分有序)、默认原型简单键值对存储、对象属性配置
Array索引 - 值(有序列表)有序、允许重复有序数据集合、按索引访问、批量操作数据

四、关键注意事项

  1. 引用类型作为键 / 值的注意点:Set/Map 存储引用类型时,判断重复的是引用地址,而非对象内容:

    const obj1 = { a: 1 };
    const obj2 = { a: 1 };
    const set = new Set([obj1, obj2]);
    console.log(set.size); // 2(obj1 和 obj2 引用地址不同,视为不同元素)
    
  2. Map 与 Object 的选择原则

    • 当键为非字符串 / Symbol 类型、需要保留插入顺序、动态增删大量键值对时,优先用 Map;
    • 当仅存储简单配置项、键为字符串时,用 Object 更简洁。
  3. Set 与 Array 的选择原则

    • 当需要去重、快速判断元素是否存在时,优先用 Set;
    • 当需要按索引访问、批量排序 / 筛选数据时,用 Array。

总结

  1. Set:无序不重复的「值集合」,核心用途是去重和快速判存;
  2. Map:有序键值对的「映射集合」,是 Object 的升级版,支持任意类型键,适合复杂映射场景;
  3. 二者均支持迭代器遍历,size 属性获取长度,比传统 Object/Array 更高效、更灵活;
  4. 选择逻辑:按「是否需要键值对」区分 Map/Set,按「是否需要去重 / 有序」区分 Set/Array、Map/Object。