Map
是 TypeScript 和 JavaScript 中的一种内置对象,它用于存储键值对(key-value pairs),并且可以记住键的原始插入顺序。与传统的对象字面量相比,Map
提供了更多的功能和灵活性,尤其是在处理非字符串键和动态添加或删除键值对时。以下是关于 Map
的详细介绍,包括基本用法、常用方法、类型注解以及与其他数据结构的比较。
基本用法
1. 创建一个 Map
你可以使用 new Map()
来创建一个新的 Map
实例。
let map = new Map();
2. 添加键值对
你可以使用 set
方法来向 Map
中添加键值对。
let map = new Map();
map.set("name", "Alice");
map.set("age", 30);
map.set(true, "This is true");
console.log(map); // Map(3) { 'name' => 'Alice', 'age' => 30, true => 'This is true' }
3. 获取值
你可以使用 get
方法来获取 Map
中的值。
let map = new Map([
["name", "Alice"],
["age", 30]
]);
console.log(map.get("name")); // 输出: Alice
console.log(map.get("age")); // 输出: 30
4. 检查键是否存在
你可以使用 has
方法来检查 Map
中是否包含某个键。
let map = new Map([
["name", "Alice"],
["age", 30]
]);
console.log(map.has("name")); // 输出: true
console.log(map.has("city")); // 输出: false
5. 删除键值对
你可以使用 delete
方法来删除 Map
中的键值对。
let map = new Map([
["name", "Alice"],
["age", 30]
]);
map.delete("age");
console.log(map); // Map(1) { 'name' => 'Alice' }
6. 清空 Map
你可以使用 clear
方法来清空 Map
中的所有键值对。
let map = new Map([
["name", "Alice"],
["age", 30]
]);
map.clear();
console.log(map); // Map(0) {}
常用方法
1. size
size
属性返回 Map
中键值对的数量。
let map = new Map([
["name", "Alice"],
["age", 30]
]);
console.log(map.size); // 输出: 2
2. forEach
forEach
方法允许你遍历 Map
中的每个键值对。
let map = new Map([
["name", "Alice"],
["age", 30]
]);
map.forEach((value, key) => {
console.log(`${key}: ${value}`);
});
// 输出:
// name: Alice
// age: 30
3. keys
keys
方法返回一个迭代器,用于遍历 Map
中的所有键。
let map = new Map([
["name", "Alice"],
["age", 30]
]);
for (let key of map.keys()) {
console.log(key);
}
// 输出:
// name
// age
4. values
values
方法返回一个迭代器,用于遍历 Map
中的所有值。
let map = new Map([
["name", "Alice"],
["age", 30]
]);
for (let value of map.values()) {
console.log(value);
}
// 输出:
// Alice
// 30
5. entries
entries
方法返回一个迭代器,用于遍历 Map
中的所有键值对。
let map = new Map([
["name", "Alice"],
["age", 30]
]);
for (let [key, value] of map.entries()) {
console.log(`${key}: ${value}`);
}
// 输出:
// name: Alice
// age: 30
类型注解
TypeScript 允许你为 Map
的键和值添加类型注解,以确保类型安全。
let stringNumberMap: Map<string, number> = new Map();
stringNumberMap.set("one", 1);
stringNumberMap.set("two", 2);
let anyMap: Map<any, any> = new Map();
anyMap.set("name", "Alice");
anyMap.set(true, "This is true");
anyMap.set(42, "The answer");
// 使用泛型定义更复杂的类型
type Person = { name: string; age: number };
let personMap: Map<string, Person> = new Map();
personMap.set("alice", { name: "Alice", age: 30 });
personMap.set("bob", { name: "Bob", age: 25 });
console.log(personMap.get("alice")); // 输出: { name: 'Alice', age: 30 }
与对象字面量的比较
1. 键的类型
- 对象字面量:键只能是字符串或符号(Symbol)。
Map
:键可以是任何类型的值,包括对象、函数、数字等。
let obj = {};
obj[true] = "This is true"; // 不推荐,虽然可以工作,但不符合预期
obj[42] = "The answer"; // 不推荐,虽然可以工作,但不符合预期
let map = new Map();
map.set(true, "This is true"); // 推荐
map.set(42, "The answer"); // 推荐
2. 键的顺序
- 对象字面量:键的顺序不保证,特别是对于数字键。
Map
:键的顺序是按照插入顺序保存的。
let obj = {
b: "second",
a: "first"
};
for (let key in obj) {
console.log(key); // 输出顺序不确定,可能是 "b", "a" 或 "a", "b"
}
let map = new Map([
["b", "second"],
["a", "first"]
]);
for (let [key, value] of map) {
console.log(key); // 输出: "b", "a" (按插入顺序)
}
3. 动态添加和删除键
- 对象字面量:需要手动管理属性的添加和删除。
Map
:提供了set
、get
、has
和delete
等方法,操作更加方便。
let obj = {};
obj.name = "Alice";
delete obj.name;
let map = new Map();
map.set("name", "Alice");
map.delete("name");
4. 性能
- 对象字面量:对于小规模的数据集,性能差异不大。
Map
:对于大规模的数据集,Map
的性能通常优于对象字面量,特别是在频繁添加和删除键的情况下。
高级用法
1. 使用 Map
作为缓存
Map
可以用于实现简单的缓存机制,特别是在需要根据不同的输入参数返回不同结果的情况下。
function fibonacci(n: number, cache: Map<number, number> = new Map()): number {
if (cache.has(n)) {
return cache.get(n)!;
}
if (n <= 1) {
return n;
}
const result = fibonacci(n - 1, cache) + fibonacci(n - 2, cache);
cache.set(n, result);
return result;
}
console.log(fibonacci(10)); // 输出: 55
2. 使用 Map
作为集合
虽然 Set
是专门用于存储唯一值的数据结构,但 Map
也可以用于类似的目的,特别是当你需要存储键值对时。
let uniqueItems = new Map();
uniqueItems.set("apple", 1);
uniqueItems.set("banana", 1);
uniqueItems.set("apple", 1); // 重复的键不会添加新的项
console.log(uniqueItems.size); // 输出: 2
总结
Map
是 TypeScript 和 JavaScript 中非常强大的数据结构,适用于需要存储键值对的场景。与传统的对象字面量相比,Map
提供了更多的灵活性和功能,支持任意类型的键、保持插入顺序、提供丰富的操作方法,并且在性能上也有优势。掌握 Map
的基本用法、常用方法、类型注解以及高级用法,可以帮助你在开发中编写出更健壮、更灵活的应用程序。了解如何在适当的情况下选择 Map
而不是对象字面量,可以使你的代码更加简洁和高效。
Map
是一种内置对象,它类似于对象,有以下区别。
- 但“键”的范围不限于字符串。任何值(包括对象)都可以作为一个键。
Map
保持键值对的原始插入顺序。
创建映射
使用构造函数创建
let map = new Map();
使用数组初始化
const myMap = new Map([
['key1', 'value1'],
['key2', 'value2']
]);
console.log(myMap);
字面量语法
const myMap = new Map({
key1: 'value1',
key2: 'value2'
});
console.log(myMap);
添加元素
set
const myMap = new Map();
myMap.set('a', 1);
myMap.set('b', 2);
console.log(myMap); // Map { 'a' => 1, 'b' => 2 }
删除元素
删除指定元素 delete
const myMap = new Map();
myMap.set('a', 1);
myMap.delete('a');
console.log(myMap.has('a')); // false
清空元素 clear
const myMap = new Map();
myMap.set('a', 1);
myMap.set('b', 2);
myMap.clear();
console.log(myMap.size); // 0
访问和查找元素
get
const myMap = new Map();
myMap.set('a', 1);
console.log(myMap.get('a')); // 1
console.log(myMap.get('b')); // undefined
遍历元素
forEach
const myMap = new Map();
myMap.set('a', 1);
myMap.set('b', 2);
myMap.forEach((value, key) => {
console.log(`${key}: ${value}`); // a: 1, b: 2
});
for...of 循环
const map = new Map([['a', 1], ['b', 2], ['c', 3]]);
for (const [key, value] of map) {
console.log(key + ': ' + value);
}
keys
const myMap = new Map();
myMap.set('a', 1);
myMap.set('b', 2);
const keysIterator = myMap.keys();
console.log([...keysIterator]); // [ 'a', 'b' ]
values
const myMap = new Map();
myMap.set('a', 1);
myMap.set('b', 2);
const valuesIterator = myMap.values();
console.log([...valuesIterator]); // [ 1, 2 ]
entries
const myMap = new Map();
myMap.set('a', 1);
myMap.set('b', 2);
const entriesIterator = myMap.entries();
console.log([...entriesIterator]); // [ [ 'a', 1 ], [ 'b', 2 ] ]
其他
has
const myMap = new Map();
myMap.set('a', 1);
console.log(myMap.has('a')); // true
console.log(myMap.has('b')); // false
拷贝
浅拷贝
使用构造函数
// 创建原始映射,其中包含一个对象作为值
const originalMap = new Map([
['key1', 'value1'],
['key2', { a: 3, b: [4, 5] }]
]);
// 创建浅拷贝映射
const shallowCopyMap = new Map(originalMap);
// 输出原始映射和浅拷贝映射
console.log('原始映射:', originalMap); // Map { 'key1' => 'value1', 'key2' => { a: 3, b: [ 4, 5 ] } }
console.log('浅拷贝映射:', shallowCopyMap); // Map { 'key1' => 'value1', 'key2' => { a: 3, b: [ 4, 5 ] } }
// 修改原始映射中的对象
const originalObject = originalMap.get('key2'); // 获取映射中的对象
originalObject.a = 10; // 修改对象的属性
// 输出修改后的原始映射和浅拷贝映射
console.log('修改后的原始映射:', originalMap); // Map { 'key1' => 'value1', 'key2' => { a: 10, b: [ 4, 5 ] } }
console.log('浅拷贝映射:', shallowCopyMap); // Map { 'key1' => 'value1', 'key2' => { a: 10, b: [ 4, 5 ] } }
深拷贝
使用循环和递归方法
function deepCloneMap(map) {
const newMap = new Map();
map.forEach((value, key) => {
newMap.set(key, value instanceof Object ? structuredClone(value) : value);
});
return newMap;
}
const originalMap = new Map([['key1', 'value1'], ['key2', { a: 3 }]]);
const deepCopyMap = deepCloneMap(originalMap);
使用 Lodash
const _ = require('lodash');
// 创建原始映射
const originalMap = new Map([
['key1', 'value1'],
['key2', { a: 3, b: [4, 5] }]
]);
// 深拷贝映射
const deepCopyMap = new Map(_.cloneDeep(Array.from(originalMap.entries())));
console.log(deepCopyMap); // 输出: Map { 'key1' => 'value1', 'key2' => { a: 3, b: [ 4, 5 ] } }
// 修改原始对象
const originalObject = originalMap.get('key2'); // 获取映射中的对象
originalObject.a = 10;
console.log(originalMap); // 输出: Map { 'key1' => 'value1', 'key2' => { a: 10, b: [ 4, 5 ] } }
console.log(deepCopyMap); // 输出: Map { 'key1' => 'value1', 'key2' => { a: 3, b: [ 4, 5 ] } } - 深拷贝不受影响
排序
// 创建一个 Map
const originalMap = new Map([
['key1', 5],
['key2', 3],
['key3', 8],
['key4', 1]
]);
// 将 Map 转换为数组并排序
const sortedArray = Array.from(originalMap.entries()).sort((a, b) => a[1] - b[1]);
// 输出排序后的结果
console.log('排序后的数组:', sortedArray); // 输出: [ [ 'key4', 1 ], [ 'key2', 3 ], [ 'key1', 5 ], [ 'key3', 8 ] ]
// 如果需要,可以将排序后的数组转换回 Map
const sortedMap = new Map(sortedArray);
console.log('排序后的 Map:', sortedMap);
// 输出: Map { 'key4' => 1, 'key2' => 3, 'key1' => 5, 'key3' => 8 }
反转
// 创建一个 Map
const originalMap = new Map([
['key1', 1],
['key2', 2],
['key3', 3],
]);
// 将 Map 转换为数组并反转
const reversedArray = Array.from(originalMap.entries()).reverse();
// 输出反转后的结果
console.log('反转后的数组:', reversedArray); // 输出: [ [ 'key3', 3 ], [ 'key2', 2 ], [ 'key1', 1 ] ]
// 如果需要,可以将反转后的数组转换回 Map
const reversedMap = new Map(reversedArray);
console.log('反转后的 Map:', reversedMap);
// 输出: Map { 'key3' => 3, 'key2' => 2, 'key1' => 1 }
属性
长度 size
返回 Map
中键值对的数量。
const myMap = new Map();
myMap.set('a', 1);
myMap.set('b', 2);
console.log(myMap.size); // 2