# Map

作者: 多叉树;转载须注明出处

# Map是什么

Map 是 ECMAScript 6 (ES6) 引入的一种新的数据结构。

它类似于对象,也是键值对的集合,但是使用上更加灵活和强大。

Map 允许任何类型的值(包括对象)作为键或值。

// 创建一个新的 Map
let myMap = new Map();

// 添加键值对
myMap.set('name', '张三');
myMap.set(1, 'number one');
myMap.set(true, 'boolean');

// 获取值
console.log(myMap.get('name')); // 输出:张三
console.log(myMap.get(1)); // 输出:number one
console.log(myMap.get(true)); // 输出:boolean

# 方法和属性

size 返回 Map对象中的键值对数量
set(key, value) 向 Map中添加或更新一个指定的键值对
get(key) 返回一个指定键的值,如果不存在,则返回 undefined
has(key) 返回一个布尔值,表示 Map 中是否存在指定的键
delete(key) 从 Map 中移除指定的键值对
clear() 移除 Map 中的所有键值对
keys() 返回一个包含 Map 中所有键的迭代器
values() 返回一个包含 Map 中所有值的迭代器
entries() 返回一个包含 Map 中所有键值对的迭代器
forEach(callbackFn, thisArg) 对 Map 中的每个键值对执行指定的回调函数

# 基本使用举例

let fruits = new Map();

// 使用 set() 添加键值对
fruits.set('apple', 5);
fruits.set('banana', 3);
fruits.set('orange', 2);

console.log(fruits.size); // 输出:3

console.log(fruits.get('banana')); // 输出:3

console.log(fruits.has('grape')); // 输出:false

fruits.delete('orange');
console.log(fruits.size); // 输出:2

// 遍历 Map
fruits.forEach((value, key, map) => {
    console.log(key, value);
    console.log(map) // fruits 本身
    console.log(this) // this Arg document
}, document);

// 使用 for...of 遍历
for (let [fruit, quantity] of fruits) {
    console.log(fruit, quantity);
}

fruits.clear();
console.log(fruits.size); // 输出:0

# Map和对象的区别

对象 Map
键的类型(Key) 键只能是字符串或 Symbol 键可以是任何类型,包括函数、对象或任意基本类型
键的顺序 键没有固定顺序 键是有序的。当遍历时,Map 对象以插入的顺序返回键值对
大小(Size) 需要手动计算大小 可以通过 size 属性直接获取
迭代 需要通过其他方法如 Object.keys() 来获取键然后进行迭代 是可迭代的,可以直接进行迭代
性能 在频繁增删键值对的场景下表现一般 在频繁增删键值对时表现更好

示例对比

// 对象
let obj = {
    name: '李四',
    1: 'number one',
    true: 'boolean'
};

console.log(obj.name); // 输出:李四
console.log(obj['1']); // 输出:number one
console.log(obj.true); // 输出:boolean

// Map
let map = new Map([
    ['name', '李四'],
    [1, 'number one'],
    [true, 'boolean']
]);

console.log(map.get('name')); // 输出:李四
console.log(map.get(1)); // 输出:number one
console.log(map.get(true)); // 输出:boolean

// 大小比较
console.log(Object.keys(obj).length); // 手动计算对象大小
console.log(map.size); // 直接获取 Map 大小

// 迭代比较
// 对象迭代
for (let [key, value] of Object.entries(obj)) {
    console.log(key, value);
}

// Map 迭代
for (let [key, value] of map) {
    console.log(key, value);
}

# 构造函数

Map 的构造函数允许我们在创建 Map 实例时初始化键值对。我们可以传入一个可迭代对象(如数组),其中每个元素都是一个键值对数组。

语法

new Map([iterable])

其中 iterable 是一个数组(或者其他可迭代对象),其元素是键值对(两个元素的数组)。每个键值对会被加到新的 Map 中。

举例

// 使用数组创建 Map
let map1 = new Map([
  ['key1', 'value1'],
  ['key2', 'value2']
]);

console.log(map1.get('key1')); // 输出:value1

// 使用另一个 Map 创建新的 Map
let map2 = new Map(map1);

console.log(map2.get('key2')); // 输出:value2

// 创建空 Map
let emptyMap = new Map();

console.log(emptyMap.size); // 输出:0

# 注意事项

  • 键的唯一性 Map 中的每个键都必须是唯一的。如果多次使用相同的键设置值,后面的值会覆盖前面的值。
  • 键的比较 Map 使用 SameValueZero 算法来比较键的相等性。这意味着 NaN 被视为等于 NaN(虽然 NaN !== NaN),而 +0 和 -0 被视为相等。
  • 引用类型作为键 当使用对象作为键时,要注意引用相等性。两个看起来相同但分别定义的对象会被视为不同的键。
  • 迭代顺序 Map 保持键的插入顺序。在迭代时,键值对会按照插入的顺序返回。
  • 性能考虑 对于大量数据或频繁增删操作,Map 通常比普通对象有更好的性能表现。
  • 序列化 Map 对象不能直接被 JSON.stringify() 序列化。如需序列化,需要先将 Map 转换为数组或普通对象。

举例

// 引用类型作为键的注意事项
let keyObj1 = {id: 1};
let keyObj2 = {id: 1};
let myMap = new Map();

myMap.set(keyObj1, 'value1');
myMap.set(keyObj2, 'value2');

console.log(myMap.size); // 输出:2,因为 keyObj1 和 keyObj2 是不同的对象引用

// 序列化 Map
let map = new Map([['key1', 'value1'], ['key2', 'value2']]);
let jsonString = JSON.stringify([...map]);
console.log(jsonString); // 输出:[["key1","value1"],["key2","value2"]]

# 总结

Map 适用于需要非字符串类型键、保证插入顺序、频繁增删操作或需要键值对操作的场景。它提供了更强大、灵活的 API 和性能优势,因此在处理复杂键值对数据时是不错的选择。

扫码关注(有广告,介意请勿关注)