一、什么是 Map?
Map 是 ES6(ECMAScript 2015)引入的一种新的数据结构,它类似于 JavaScript 中的对象,都是键值对的集合。但 Map 相比传统对象提供了更强大、更灵活的功能。
1.1 Map 的核心特点
- 键的类型无限制:对象的键只能是字符串或 Symbol,而 Map 的键可以是任意类型(字符串、数字、对象、函数、数组等)
- 保持插入顺序:Map 中的键值对按照插入顺序排列,便于有序遍历
- 内置遍历方法:提供了专门的遍历方法,使用更加方便
- 动态大小 :通过
size属性直接获取键值对数量,无需手动计算
二、Map 的基本语法
2.1 创建和基本操作
javascript
// 创建 Map 实例
const map = new Map();
// 添加键值对
map.set('name', '张三');
map.set(1, '数字键');
map.set({ id: 1 }, '对象键');
map.set(() => {}, '函数键');
// 获取值
console.log(map.get('name')); // 输出: 张三
// 检查键是否存在
console.log(map.has('name')); // 输出: true
// 删除键值对
map.delete('name');
// 清空 Map
map.clear();
// 获取大小
console.log(map.size); // 输出当前键值对数量
2.2 构造函数初始化
javascript
// 通过数组初始化 Map
const map = new Map([
['name', '李四'],
['age', 25],
['city', '北京']
]);
console.log(map.get('name')); // 输出: 李四
三、Map 与 Object 的对比
| 特性 | Map | Object |
|---|---|---|
| 键的类型 | 任意类型 | 字符串或 Symbol |
| 键的顺序 | 保持插入顺序 | 不保证顺序 |
| 大小获取 | size 属性 |
需手动计算 Object.keys(obj).length |
| 性能 | 频繁增删时性能更好 | 频繁增删时性能较差 |
| 迭代 | 内置迭代器 | 需要手动转换 |
| 序列化 | 不支持 JSON 序列化 | 支持 JSON 序列化 |
四、Map 的遍历方法
4.1 基本遍历
javascript
const map = new Map([
['name', '王五'],
['age', 30],
['job', '工程师']
]);
// 1. forEach 遍历
map.forEach((value, key) => {
console.log(`${key}: ${value}`);
});
// 2. for...of 遍历 entries()
for (const [key, value] of map.entries()) {
console.log(`${key}: ${value}`);
}
// 3. for...of 遍历 keys()
for (const key of map.keys()) {
console.log(key);
}
// 4. for...of 遍历 values()
for (const value of map.values()) {
console.log(value);
}
// 5. 直接遍历 Map(等同于遍历 entries())
for (const [key, value] of map) {
console.log(`${key}: ${value}`);
}
4.2 转换为数组
javascript
// 转换为键值对数组
const entriesArray = Array.from(map.entries());
// 或使用展开运算符
const entriesArray2 = [...map];
// 转换为键数组
const keysArray = Array.from(map.keys());
// 转换为值数组
const valuesArray = Array.from(map.values());
五、Map 在实际项目中的应用
5.1 请求管理:防止重复请求
在前端项目中,使用 Map 来处理重复请求业务是一个非常典型的应用场景。
javascript
// 存储每个请求的取消函数
const pendingMap = new Map();
// 生成请求的唯一标识
const getRequestKey = (config) => {
const { method, url, params, data } = config;
return [method, url, params, JSON.stringify(data)].join('&');
};
// 在请求拦截器中使用
instance.interceptors.request.use(function (config) {
const key = getRequestKey(config);
// 检查是否有相同的请求正在进行
if (pendingMap.has(key)) {
const cancel = pendingMap.get(key);
cancel('取消重复请求'); // 取消之前的请求
pendingMap.delete(key);
}
// 创建新的取消令牌并存储
config.cancelToken = new axios.CancelToken(cancel => {
pendingMap.set(key, cancel);
});
return config;
});
为什么使用 Map 而不是 Object?
- 键的类型灵活:请求的唯一标识可能是复杂的字符串,Map 处理更方便
- 快速查找 :
has()和get()方法的时间复杂度为 O(1),查找效率高 - 易于管理 :可以直接使用
delete()方法删除特定请求,无需遍历
5.2 缓存管理
javascript
// 使用 Map 实现简单的缓存
const cache = new Map();
function getData(key) {
// 检查缓存
if (cache.has(key)) {
console.log('从缓存中获取数据');
return cache.get(key);
}
// 模拟异步获取数据
const data = fetchFromAPI(key);
cache.set(key, data); // 存入缓存
return data;
}
// 设置缓存过期时间
const cacheWithExpiry = new Map();
function setCacheWithExpiry(key, value, ttl = 60000) {
const now = new Date();
const item = {
value,
expiry: now.getTime() + ttl
};
cacheWithExpiry.set(key, item);
}
function getCacheWithExpiry(key) {
const item = cacheWithExpiry.get(key);
if (!item) return null;
const now = new Date();
if (now.getTime() > item.expiry) {
cacheWithExpiry.delete(key); // 过期删除
return null;
}
return item.value;
}
5.3 状态管理
javascript
// 使用 Map 管理组件状态
const componentStates = new Map();
function setComponentState(componentId, state) {
componentStates.set(componentId, state);
}
function getComponentState(componentId) {
return componentStates.get(componentId);
}
function removeComponentState(componentId) {
componentStates.delete(componentId);
}
// 批量更新状态
function updateMultipleStates(updates) {
updates.forEach(([componentId, state]) => {
componentStates.set(componentId, state);
});
}
六、Map 的最佳实践
6.1 选择使用 Map 的场景
- 需要频繁增删键值对:Map 的增删操作性能优于 Object
- 键的类型不限于字符串:需要使用对象、函数等作为键
- 需要保持插入顺序:Map 保证键值对的插入顺序
- 需要快速查找:Map 的查找操作时间复杂度为 O(1)
- 需要知道集合大小 :直接使用
size属性
6.2 避免使用 Map 的场景
- 需要 JSON 序列化:Map 不支持 JSON 序列化,需要手动转换
- 简单的键值对存储:如果只是简单的字符串键,使用 Object 更合适
- 需要原型链继承:Map 没有原型链,无法利用原型继承特性
6.3 性能优化建议
javascript
// 1. 预先设置 Map 大小(虽然 Map 没有预分配方法,但可以合理规划)
const largeMap = new Map();
// 2. 避免频繁的 Map 操作
// 不好的做法
for (let i = 0; i < 10000; i++) {
largeMap.set(i, i * 2);
}
// 好的做法:批量操作
const entries = Array.from({ length: 10000 }, (_, i) => [i, i * 2]);
entries.forEach(([key, value]) => largeMap.set(key, value));
// 3. 及时清理不需要的键值对
function cleanupMap(map, maxAge = 3600000) {
const now = Date.now();
for (const [key, value] of map) {
if (value.timestamp && now - value.timestamp > maxAge) {
map.delete(key);
}
}
}
七、总结
Map 是一个强大且灵活的数据结构,在现代 JavaScript 开发中有着广泛的应用。通过合理使用 Map,我们可以:
- 提升代码可读性:清晰的 API 设计使代码更易理解
- 提高性能:高效的查找和操作性能
- 简化复杂逻辑:特别是在请求管理、缓存、状态管理等场景
一键三连支持下吧 ~~~