JavaScript Map 深度解析:用法、特点与示例 – wiki大全

JavaScript Map 深度解析:用法、特点与示例

在现代 JavaScript 开发中,Map 是一种强大且灵活的数据结构,它允许我们存储键值对,并且在许多方面比传统的 JavaScript 对象更具优势。本文将深入探讨 JavaScript Map 的用法、独特特点以及通过代码示例展示其强大功能。


什么是 JavaScript Map?

Map 对象是 ES6 (ECMAScript 2015) 引入的一种新的键值对集合。与 Object 类似,它存储键值对,但 Map 的主要特点是:

  1. 任意类型的键: Map 的键可以是任意数据类型(包括对象、函数、NaN 等),而 Object 的键只能是字符串或 Symbol。
  2. 保持插入顺序: Map 会记住键值对的插入顺序,这意味着当你迭代 Map 时,它们会按照添加时的顺序返回。
  3. 可直接获取大小: Map 提供了一个 size 属性,可以直接获取其中键值对的数量,无需手动计算。
  4. 更好的性能: 在频繁添加和删除键值对的场景下,Map 通常比 Object 表现出更好的性能。

Map 的基本用法

1. 创建 Map

可以通过 new Map() 构造函数来创建一个新的 Map

javascript
const myMap = new Map();
console.log(myMap); // Map(0) {}

你也可以在创建时传入一个可迭代对象(如数组),其中每个元素都是一个包含两个元素的数组([key, value])。

javascript
const initialData = [
['name', 'Alice'],
['age', 30],
[true, 'is active']
];
const userMap = new Map(initialData);
console.log(userMap); // Map(3) { 'name' => 'Alice', 'age' => 30, true => 'is active' }

2. 添加和更新元素 (set())

使用 set(key, value) 方法向 Map 中添加或更新键值对。如果键已存在,其值将被更新;如果键不存在,则会添加新的键值对。

“`javascript
const userProfile = new Map();

userProfile.set(‘id’, 123);
userProfile.set(‘username’, ‘js_dev’);
userProfile.set(’email’, ‘[email protected]’);

console.log(userProfile);
// Map(3) { ‘id’ => 123, ‘username’ => ‘js_dev’, ’email’ => ‘[email protected]’ }

// 更新一个值
userProfile.set(’email’, ‘[email protected]’);
console.log(userProfile.get(’email’)); // [email protected]
`set()` 方法可以链式调用:javascript
const chainMap = new Map()
.set(‘a’, 1)
.set(‘b’, 2)
.set(‘c’, 3);
console.log(chainMap); // Map(3) { ‘a’ => 1, ‘b’ => 2, ‘c’ => 3 }
“`

3. 获取元素 (get())

使用 get(key) 方法根据键获取对应的值。如果键不存在,则返回 undefined

javascript
console.log(userProfile.get('username')); // js_dev
console.log(userProfile.get('password')); // undefined

4. 检查键是否存在 (has())

使用 has(key) 方法检查 Map 中是否存在某个键,返回 truefalse

javascript
console.log(userProfile.has('id')); // true
console.log(userProfile.has('address')); // false

5. 删除元素 (delete())

使用 delete(key) 方法从 Map 中删除指定的键值对。如果删除成功(即该键存在),返回 true;否则返回 false

javascript
console.log(userProfile.delete('email')); // true
console.log(userProfile.has('email')); // false
console.log(userProfile.delete('unknown')); // false

6. 获取 Map 的大小 (size)

size 属性返回 Map 中键值对的数量。

javascript
console.log(userProfile.size); // 2 (id, username)

7. 清空 Map (clear())

使用 clear() 方法删除 Map 中的所有键值对。

javascript
userProfile.clear();
console.log(userProfile.size); // 0
console.log(userProfile); // Map(0) {}


Map 的独特特点与高级用法

1. 键的类型可以是任意值

这是 Map 相对于 Object 的最大优势之一。你可以使用对象、函数甚至 nullundefined 作为键。

“`javascript
const myMap = new Map();

const objKey = { a: 1 };
const funcKey = () => {};

myMap.set(‘string’, ‘value’);
myMap.set(1, ‘number value’);
myMap.set(true, ‘boolean value’);
myMap.set(objKey, ‘object value’);
myMap.set(funcKey, ‘function value’);
myMap.set(NaN, ‘Not a Number’); // NaN 被认为是相同的值

console.log(myMap.get(objKey)); // object value
console.log(myMap.get(funcKey)); // function value
console.log(myMap.get(NaN)); // Not a Number
console.log(myMap.get(1)); // number value
``
**注意**:
Map在比较键时使用 SameValueZero 算法。这意味着NaN被视为与其自身相等,这在Object中是做不到的 ({ NaN: ‘test’ },然后obj[NaN]` 将无法工作)。

2. 保持插入顺序

当你迭代一个 Map 时,它会按照键值对被插入的顺序进行遍历。这对于需要维护数据顺序的场景非常有用。

“`javascript
const orderedMap = new Map([
[‘first’, 1],
[‘second’, 2],
[‘third’, 3]
]);

for (const [key, value] of orderedMap) {
console.log(${key}: ${value});
}
// 输出:
// first: 1
// second: 2
// third: 3
“`

3. 迭代 Map

Map 对象是可迭代的,这意味着你可以使用 for...of 循环、forEach() 方法以及 keys()values()entries() 方法来遍历它。

  • for...of 循环: 默认迭代 [key, value] 对。

    javascript
    const myMap = new Map([['a', 1], ['b', 2]]);
    for (const [key, value] of myMap) {
    console.log(`${key} => ${value}`);
    }
    // a => 1
    // b => 2

  • forEach(callbackFn, thisArg): 遍历 Map 中的每个元素。

    javascript
    myMap.forEach((value, key, map) => {
    console.log(`Key: ${key}, Value: ${value}`);
    });
    // Key: a, Value: 1
    // Key: b, Value: 2

  • keys(): 返回一个包含 Map 中所有键的迭代器。

    javascript
    for (const key of myMap.keys()) {
    console.log(key);
    }
    // a
    // b

  • values(): 返回一个包含 Map 中所有值的迭代器。

    javascript
    for (const value of myMap.values()) {
    console.log(value);
    }
    // 1
    // 2

  • entries(): 返回一个包含 Map 中所有 [key, value] 对的迭代器(与 for...of 默认行为相同)。

    javascript
    for (const entry of myMap.entries()) {
    console.log(entry); // [ 'a', 1 ], [ 'b', 2 ]
    }

4. Map 与 Array/Object 之间的转换

  • Map 转换为 Array:

    “`javascript
    const myMap = new Map([[‘a’, 1], [‘b’, 2]]);
    const keysArray = […myMap.keys()]; // [‘a’, ‘b’]
    const valuesArray = […myMap.values()]; // [1, 2]
    const entriesArray = […myMap.entries()]; // [[‘a’, 1], [‘b’, 2]]
    const anotherEntriesArray = Array.from(myMap); // [[‘a’, 1], [‘b’, 2]]

    console.log(keysArray);
    console.log(valuesArray);
    console.log(entriesArray);
    console.log(anotherEntriesArray);
    “`

  • Object 转换为 Map:

    javascript
    const myObject = { name: 'Bob', age: 25 };
    const mapFromObject = new Map(Object.entries(myObject));
    console.log(mapFromObject); // Map(2) { 'name' => 'Bob', 'age' => 25 }

  • Map 转换为 Object:
    这需要确保 Map 的所有键都是字符串或 Symbol,否则可能会丢失数据。

    “`javascript
    const myMap = new Map([[‘name’, ‘Bob’], [‘age’, 25]]);
    const objFromMap = Object.fromEntries(myMap);
    console.log(objFromMap); // { name: ‘Bob’, age: 25 }

    const mapWithNonStringKey = new Map([[1, ‘one’], [‘two’, 2]]);
    // Object.fromEntries(mapWithNonStringKey) 会将数字键自动转换为字符串
    console.log(Object.fromEntries(mapWithNonStringKey)); // { ‘1’: ‘one’, two: 2 }

    const mapWithObjectKey = new Map([[{id: 1}, ‘value’]]);
    // Object.fromEntries 无法处理对象作为键,会报错或导致非预期行为
    // console.log(Object.fromEntries(mapWithObjectKey)); // TypeError: Cannot convert a Symbol value to a string
    “`


Map 与 Object 的选择

  • 使用 Map 的场景:

    • 需要使用非字符串或 Symbol 作为键(例如 DOM 元素、对象、函数)。
    • 需要保持键值对的插入顺序。
    • 需要频繁地添加或删除键值对,且性能是关键考虑因素。
    • 需要方便地获取集合的大小 (.size)。
    • 键值对的数量可能会很大,需要优化性能。
  • 使用 Object 的场景:

    • 只使用字符串或 Symbol 作为键。
    • 需要 JSON 序列化(Map 不直接支持)。
    • 需要利用对象字面量 {} 的简洁语法。
    • 用于存储结构化数据,其中键是已知的属性名。

结论

JavaScript Map 提供了比传统 Object 更灵活的键类型和更清晰的 API,尤其是在处理动态键和需要保持数据顺序的场景中。理解 Map 的用法和特点,能够帮助开发者编写更健壮、高效且易于维护的 JavaScript 代码。在面对键值对存储需求时,根据实际的应用场景权衡 MapObject 的优缺点,选择最合适的数据结构是至关重要的。

滚动至顶部