TypeScript 中 Map 的全面指南:从基础到高级应用
Map 是 TypeScript/JavaScript 中一种强大的集合类型,它提供了比普通对象更灵活的键值对存储能力。本文将全面介绍 Map 的各种用法,特别是遍历操作的详细应用。
什么是 Map?
Map 是一种键值对集合,与普通对象(Object)相比,它有以下优势:
-
键可以是任意数据类型(对象、数组、函数等)
-
维护元素的插入顺序
-
提供了一组方便的遍历方法
-
有专门的 size 属性获取元素数量
-
在频繁增删操作的场景下性能更好
基本操作
创建和初始化 Map
// 创建空 Map
const emptyMap = new Map<string, number>();// 带初始值的 Map
const initializedMap = new Map<string, string>([['name', 'Alice'],['title', 'Engineer']
]);// 使用不同类型作为键
const complexKeyMap = new Map<object, number>();
const user = { id: 1 };
complexKeyMap.set(user, 100);
增删改查操作
const map = new Map<string, number>();// 添加/更新
map.set('age', 30);
map.set('score', 95);// 获取
console.log(map.get('age')); // 30// 检查存在
console.log(map.has('age')); // true// 删除
map.delete('score');// 清空
map.clear();// 获取大小
console.log(map.size); // 0
深入探索 Map 的遍历方法
Map 提供了多种遍历方式,各有适用场景。下面通过一个完整的用户数据示例来详细说明。
示例数据准备
const userMap = new Map<number, {name: string, role: string, joinDate: Date}>();userMap.set(1001, {name: 'Alice', role: 'Admin', joinDate: new Date('2020-01-15')});
userMap.set(1002, {name: 'Bob', role: 'Developer', joinDate: new Date('2021-03-22')});
userMap.set(1003, {name: 'Charlie', role: 'Designer', joinDate: new Date('2022-07-10')});
1. for...of 循环
最直接的遍历方式,可以同时获取键和值:
for (const [id, user] of userMap) {console.log(`ID: ${id}, Name: ${user.name}, Role: ${user.role}`);
}
输出结果:
ID: 1001, Name: Alice, Role: Admin
ID: 1002, Name: Bob, Role: Developer
ID: 1003, Name: Charlie, Role: Designer
2. forEach 方法
更函数式的遍历方式,适合链式调用:
userMap.forEach((user, id) => {console.log(`${user.name} joined on ${user.joinDate.toLocaleDateString()}`);
});
输出结果:
Alice joined on 1/15/2020
Bob joined on 3/22/2021
Charlie joined on 7/10/2022
3. 分别遍历键和值
遍历键 (keys())
for (const id of userMap.keys()) {console.log(`User ID: ${id}`);
}
输出结果:
User ID: 1001
User ID: 1002
User ID: 1003
遍历值 (values())
for (const user of userMap.values()) {console.log(`Welcome ${user.name} (${user.role})`);
}
输出结果:
Welcome Alice (Admin)
Welcome Bob (Developer)
Welcome Charlie (Designer)
4. entries() 方法
返回迭代器,与直接遍历 Map 效果相同:
for (const entry of userMap.entries()) {console.log(entry); // 每个entry是[id, user]数组
}
5. 转换为数组后处理
有时先将 Map 转为数组更方便:
// 转换为键值对数组
const userArray = Array.from(userMap);
console.log(userArray);// 使用数组方法处理
const adminUsers = Array.from(userMap).filter(([id, user]) => user.role === 'Admin').map(([id, user]) => user.name);console.log('Admin users:', adminUsers); // ["Alice"]
实际应用案例
案例1:权限管理系统
// 定义角色权限映射
const rolePermissions = new Map<string, Set<string>>([['admin', new Set(['create', 'read', 'update', 'delete'])],['editor', new Set(['create', 'read', 'update'])],['viewer', new Set(['read'])]
]);// 检查权限
function hasPermission(role: string, permission: string): boolean {return rolePermissions.get(role)?.has(permission) || false;
}// 获取所有角色权限描述
function getAllRolePermissions(): string[] {const descriptions: string[] = [];rolePermissions.forEach((permissions, role) => {const permList = Array.from(permissions).join(', ');descriptions.push(`${role}: ${permList}`);});return descriptions;
}
案例2:缓存系统
class SimpleCache<T> {private cache = new Map<string, {data: T, timestamp: number}>();private readonly ttl: number; // 缓存存活时间(毫秒)constructor(ttl: number = 60_000) {this.ttl = ttl;}set(key: string, value: T): void {this.cache.set(key, {data: value,timestamp: Date.now()});}get(key: string): T | undefined {const entry = this.cache.get(key);if (!entry) return undefined;// 检查是否过期if (Date.now() - entry.timestamp > this.ttl) {this.cache.delete(key);return undefined;}return entry.data;}// 定期清理过期缓存cleanup(): void {const now = Date.now();this.cache.forEach((entry, key) => {if (now - entry.timestamp > this.ttl) {this.cache.delete(key);}});}
}
Map 与 Object 的选择
虽然 Map 更强大,但普通对象仍有其适用场景:
使用 Map 当:
-
键是动态的或未知的
-
键不是字符串/符号
-
需要频繁增删键值对
-
需要维护插入顺序
-
需要遍历操作
使用 Object 当:
-
键是静态的、已知的字符串
-
需要JSON序列化
-
需要与大量现有对象操作的代码交互
-
需要原型继承
总结
TypeScript 中的 Map 是一个功能强大的集合类型,特别适合需要复杂键类型或需要维护插入顺序的场景。通过本文的详细示例,特别是关于遍历的各种方法,你应该能够:
-
熟练使用 Map 进行数据存储和检索
-
根据场景选择合适的遍历方式
-
理解 Map 与普通对象的区别
-
在实际项目中应用 Map 解决复杂问题
掌握 Map 的使用将使你的 TypeScript 代码更加灵活和高效。