Map、Set数据类型的作用(原生JS及VUE3)

481 阅读7分钟

Map、Set数据类型的作用

在 Vue 3 的响应式系统中,MapSet 是 JavaScript 原生的集合数据类型,它们的引入是为了支持更复杂的响应式数据操作场景。

其中 Map 和 Set 作为JavaScript 原生的集合数据类型有什么作用

在 JavaScript 中,MapSet 是两种原生的集合数据类型,它们提供了比传统对象(Object)和数组(Array)更灵活、高效的数据管理方式。以下是它们的作用、特点及典型使用场景:

1. Map(映射)

作用

Map 是一种键值对(Key-Value)集合,支持任意类型的键(包括对象、函数等),并保持键的插入顺序。

核心特点

  • 键的多样性:键可以是任意数据类型(如对象、函数、Symbol),而普通对象的键只能是字符串或 Symbol。

  • 顺序性Map 会记录键值对的插入顺序,遍历时按插入顺序返回。

  • 高效操作:提供 set(key, value)get(key)has(key)delete(key) 等方法,操作时间复杂度接近 O(1)。

  • 直接获取大小:通过 size 属性直接获取键值对数量,无需手动计算。

使用场景

  1. 动态键值对管理
    需要频繁增删键值对,或键的类型复杂(如用对象作为键):
const userSessions = new Map();
const user = { id: 1 };
userSessions.set(user, { lastActive: Date.now() }); // 键是对象
  1. 维护插入顺序
    需要按顺序处理键值对(如日志记录、操作历史):
const history = new Map();
history.set(1, "操作1");
history.set(2, "操作2");
// 遍历顺序为 1 → 2
  1. 避免键名冲突
    当键名可能重复或需要唯一性时(如管理动态生成的唯一标识符):
const cache = new Map();
function getData(key) {
  if (!cache.has(key)) {
    cache.set(key, fetchData(key)); // 避免重复请求
  }
  return cache.get(key);
}

2. Set(集合)

作用

Set 是一种存储唯一值的集合(值不重复),支持快速查找和去重。

核心特点

  • 值唯一性:自动过滤重复值,确保集合内元素唯一。

  • 顺序性:记录值的插入顺序,遍历时按插入顺序返回。

  • 高效操作:提供 add(value)has(value)delete(value) 等方法,操作时间复杂度接近 O(1)。

  • 直接获取大小:通过 size 属性直接获取元素数量。

使用场景

  1. 去重
    快速去除数组中的重复项:
const numbers = [1, 2, 2, 3, 3];
const uniqueNumbers = [...new Set(numbers)]; // [1, 2, 3]
  1. 成员关系检查
    快速判断某个值是否存在:
const permissions = new Set(["read", "write"]);
if (permissions.has("read")) {
  // 允许访问
}
  1. 交集、并集、差集运算
    实现集合间的逻辑操作:
const setA = new Set([1, 2, 3]);
const setB = new Set([2, 3, 4]);
// 交集
const intersection = new Set([...setA].filter(x => setB.has(x))); // {2, 3}
// 并集
const union = new Set([...setA, ...setB]); // {1, 2, 3, 4}
  1. 跟踪唯一状态
    管理用户选择的选项、标签等需要唯一性的场景:
const selectedItems = new Set();
selectedItems.add(1001); // 添加选中项
selectedItems.delete(1001); // 取消选中

3. Map vs ObjectSet vs Array

为什么用 Map 而不是 Object

场景Map 优势
键的类型支持对象、函数等任意类型,而 Object 的键只能是字符串或 Symbol。
顺序性保留插入顺序,Object 的键顺序不可靠(ES6 后虽有序,但仍有特殊情况)。
性能频繁增删键值对时,Map 性能更优。
大小获取直接通过 size 获取,而 Object 需要 Object.keys(obj).length。

为什么用 Set 而不是 Array

场景Set 优势
去重自动去重,无需手动遍历数组检查重复。
存在性检查has(value) 的时间复杂度为 O(1),而数组的 includes() 是 O(n)。
唯一性约束天然保证值唯一,避免逻辑错误。

4. 常用操作方法

Map 的常用方法

const map = new Map();
map.set("key", "value");      // 添加键值对
map.get("key");               // 获取值 → "value"
map.has("key");               // 检查键是否存在 → true
map.delete("key");            // 删除键值对
map.clear();                  // 清空所有键值对
map.size;                     // 获取大小
for (const [key, value] of map) { ... } // 遍历

Set 的常用方法

const set = new Set();
set.add("value");             // 添加值
set.has("value");             // 检查值是否存在 → true
set.delete("value");          // 删除值
set.clear();                  // 清空集合
set.size;                     // 获取大小
for (const value of set) { ... } // 遍历

5. 注意事项

  1. 引用类型值的唯一性
    SetMap 判断值是否重复时,使用严格相等(===)。对于对象,即使内容相同,不同引用也会视为不同值:
const set = new Set();
set.add({}); 
set.add({}); 
console.log(set.size); // 2(两个空对象引用不同)
  1. 遍历性能
    MapSet 的遍历效率与数组相当,但语法更简洁(直接使用 for...of)。

  2. 兼容性
    MapSet 是 ES6 特性,现代浏览器均支持,旧环境(如 IE11)需要 polyfill(如 Babel 的 core-js)。

总结

  • Map:适合需要复杂键类型动态键值对管理顺序敏感的场景。

  • Set:适合需要去重快速存在性检查唯一值管理的场景。

它们弥补了传统 ObjectArray 的不足,提供了更现代化、高效的数据管理工具,尤其在处理动态数据和复杂逻辑时优势明显。

在vue3中的作用

Vue 3 的响应式系统基于 Proxy,能够更自然地处理这些集合类型,而 Vue 2 的 Object.defineProperty 对这些数据结构的支持非常有限。以下是它们的作用和具体使用场景:

1. Map 的作用

Map 是一种键值对的集合,支持任意类型的键(如对象、函数等),而传统对象的键只能是字符串或 Symbol。在 Vue 3 响应式系统中:

  • 支持键值对的动态增删
    Vue 3 能够追踪 Mapsetdeleteclear 等操作,触发视图更新。

  • 深层响应式
    如果 Map 的值是对象或数组,Vue 会自动递归代理,保持嵌套数据的响应性。

  • 替代响应式对象的复杂键需求
    当需要以非字符串类型(如对象)作为键时,Map 是更合适的选择。

import { reactive } from 'vue';

const state = reactive({
  userMap: new Map(), // 响应式 Map
});

// 动态添加键值对(会触发更新)
state.userMap.set(1, { name: 'Alice' });

// 删除键(会触发更新)
state.userMap.delete(1);

2. Set 的作用

Set 是一种唯一值的集合,常用于去重或管理一组不重复的数据。在 Vue 3 响应式系统中:

  • 支持值的动态增删
    Vue 3 能追踪 Setadddeleteclear 等操作。

  • 高效管理唯一性集合
    自动处理重复值,适合需要维护唯一列表的场景(如选中项、标签等)。

  • 深层响应式
    如果 Set 的值是对象或数组,同样支持深层响应式。

import { reactive } from 'vue';

const state = reactive({
  selectedIds: new Set(), // 响应式 Set
});

// 添加值(会触发更新)
state.selectedIds.add(1);

// 删除值(会触发更新)
state.selectedIds.delete(1);

3. 与 Vue 2 的对比

  • Vue 2

    • 无法直接响应 MapSet 的变化(需手动调用 Vue.setthis.$set)。

    • 对集合的深层嵌套数据支持较差。

  • Vue 3

    • 通过 Proxy 直接代理 MapSet,支持原生方法的响应式。

    • 自动追踪所有操作(如 adddeleteset 等),无需手动触发更新。

4. 使用场景

适合 Map 的场景

  • 需要以对象或其他复杂类型作为键的键值对集合。

  • 动态高频增删键值对的场景(如实时数据缓存)。

  • 需要维护插入顺序的键值对集合。

适合 Set 的场景

  • 需要维护唯一值的集合(如用户选择的 ID 列表)。

  • 快速查找或去重的场景(如过滤重复数据)。

5. 注意事项

  1. 直接替换整个 Map/Set 不会触发响应式更新
// ❌ 错误:直接替换整个 Map
state.userMap = new Map([...state.userMap, [2, { name: 'Bob' }]]);

// ✅ 正确:使用原生方法修改
state.userMap.set(2, { name: 'Bob' });
  1. 避免在模板中直接操作 Map/Set
    应在方法或计算属性中封装操作逻辑。

  2. 兼容性
    MapSet 是 ES6 特性,需确保目标环境支持(现代浏览器或通过 Polyfill)。

6. 源码中的实现

Vue 3 内部通过 collectionHandlers(位于 @vue/reactivity 模块)代理 MapSet 的操作:

  • 拦截 getsetadddelete 等方法。

  • 在操作时触发依赖收集和更新通知。

总结

Vue 3 响应式系统对 MapSet 的支持,使得开发者能够:

  1. 更自然地处理动态键值对和唯一值集合。

  2. 直接使用原生 API 操作数据,无需手动触发更新。

  3. 实现深层嵌套数据的响应式。

这些特性使 MapSet 在复杂状态管理场景中(如实时协作、数据缓存、动态表单)更加高效和直观。

更多vue相关插件及后台管理模板可访问vue admin reference,代码详情请访问github