TypeScript 中的 Map 对象详解
Map 是 ES6 引入的内置集合类型,用于存储键值对(key-value pairs),与普通对象({})的最大区别是:Map 的键可以是任意类型(包括对象、函数、Symbol 等),而普通对象的键会被强制转换为字符串。
TypeScript 为 Map 提供了完整的泛型支持,能在编译时检查键和值的类型,极大提升安全性。
1. 基本声明与泛型
// 声明一个键为 string、值为 number 的 Map
let scoreMap: Map<string, number> = new Map();
// 推荐:类型推断(无需显式注解)
let userMap = new Map<string, User>(); // 键 string,值 User 类型
interface User {
name: string;
age: number;
}
let users = new Map<number, User>(); // 键为 number(例如 ID)
常见泛型形式:Map<K, V>
K:键的类型(Key)V:值的类型(Value)
2. 常用方法
| 方法 | 描述 | 示例 | 返回值 |
|---|---|---|---|
set(key, value) | 添加或更新键值对 | map.set("alice", 95); | Map 本身(可链式调用) |
get(key) | 获取值 | map.get("alice"); → 95 或 undefined | V | undefined |
has(key) | 检查键是否存在 | map.has("bob"); → true/false | boolean |
delete(key) | 删除指定键 | map.delete("alice"); | boolean(是否删除成功) |
clear() | 清空所有键值对 | map.clear(); | void |
size | 获取键值对数量 | map.size → 3 | number |
示例:
let capital = new Map<string, string>();
capital.set("China", "Beijing");
capital.set("Japan", "Tokyo");
capital.set("France", "Paris");
console.log(capital.get("Japan")); // "Tokyo"
console.log(capital.has("USA")); // false
console.log(capital.size); // 3
capital.delete("France");
capital.clear();
3. 遍历 Map
Map 是可迭代对象,支持多种遍历方式:
let fruitCount = new Map<string, number>([
["apple", 5],
["banana", 3],
["orange", 8]
]);
// 1. 遍历键值对(最常用)
for (const [key, value] of fruitCount) {
// key 类型 string, value 类型 number
console.log(`${key}: ${value}`);
}
// 2. 遍历键
for (const key of fruitCount.keys()) {
console.log(key); // "apple", "banana", "orange"
}
// 3. 遍历值
for (const value of fruitCount.values()) {
console.log(value); // 5, 3, 8
}
// 4. 遍历条目(entries)
for (const entry of fruitCount.entries()) {
console.log(entry); // ["apple", 5] 等
}
// 5. forEach
fruitCount.forEach((value, key) => {
console.log(`${key} 有 ${value} 个`);
});
TS 类型安全:遍历时 key 和 value 的类型自动正确推断。
4. 使用对象作为键(Map 的独特优势)
普通对象 {} 不能用对象作为键(会 toString() 成 “[object Object]”),但 Map 可以:
let obj1 = { id: 1 };
let obj2 = { id: 2 };
let objMap = new Map<object, string>();
objMap.set(obj1, "第一项");
objMap.set(obj2, "第二项");
console.log(objMap.get(obj1)); // "第一项"(引用相同才匹配)
console.log(objMap.get({ id: 1 })); // undefined(新对象,引用不同)
常用于缓存、DOM 节点关联数据等场景。
5. 初始化 Map(构造函数支持可迭代对象)
// 从数组初始化
let pairs: [string, number][] = [
["math", 95],
["english", 88],
["science", 92]
];
let grades = new Map<string, number>(pairs);
// 从其他 Map 复制
let copy = new Map(grades);
6. Map vs 普通对象({})对比
| 特性 | Map | Object ({}) |
|---|---|---|
| 键的类型 | 任意类型(对象、函数、Symbol 等) | 只能是 string / symbol |
| 键的顺序 | 插入顺序保证 | ES2015+ 部分保证,但不完全可靠 |
| 大小获取 | map.size | Object.keys(obj).length |
| 遍历便利性 | 原生支持 for…of、keys()、values() | 需要 Object.keys/entries |
| 性能(频繁增删) | 更好 | 一般 |
| JSON 序列化 | 不直接支持 | 直接 JSON.stringify |
| 推荐场景 | 需要非字符串键、频繁增删、需顺序 | 简单配置、需序列化 |
7. 最佳实践建议
- 优先使用 Map 当:
- 键不是字符串(如对象、函数)。
- 需要频繁添加/删除键值对。
- 需要准确的插入顺序。
- 需要
size属性。
- 使用普通对象 当:
- 键是字符串或 symbol。
- 需要与 JSON 直接互操作。
- 数据结构简单。
- 结合 interface 定义值类型:
interface Product {
name: string;
price: number;
}
let inventory = new Map<string, Product>();
inventory.set("laptop", { name: "MacBook", price: 9999 });
小结:Map 类型速查
| 操作 | 代码示例 |
|---|---|
| 创建 | new Map<string, number>() |
| 添加 | map.set(key, value) |
| 获取 | map.get(key) |
| 检查存在 | map.has(key) |
| 删除 | map.delete(key) |
| 遍历键值 | for (const [k, v] of map) |
| 大小 | map.size |
如果您想深入了解 WeakMap(弱引用 Map,常用于缓存)、Map 与 Set 的结合使用、或 实际项目中的 Map 应用场景(如 Redux 状态管理、DOM 节点绑定数据),请告诉我!