补全JavaScript中Map的使用方法

140 阅读6分钟

Map

功能和c++的unordered_map功能差不多

Map 对象是键值对的集合。Map 中的一个键只能出现一次;它在 Map 的集合中是独一无二的。Map 对象按键值对迭代——一个 for...of 循环在每次迭代后会返回一个形式为 [key, value] 的数组。迭代按_插入顺序_进行,即键值对按 set() 方法首次插入到集合中的顺序(也就是说,当调用 set() 时,map 中没有具有相同值的键)进行迭代。

规范要求 map 实现“平均访问时间与集合中的元素数量呈次线性关系”。因此,它可以在内部表示为哈希表(使用 O(1) 查找)、搜索树(使用 O(log(N)) 查找)或任何其他数据结构,只要复杂度小于 O(N)。

构造方法

map() 语法

new Map()
new Map(iterable)

备注: Map() 只能用 new 构造。尝试不使用 new 调用它会抛出 TypeError

参数 iterable 可选 一个元素是键值对的数组或其他可迭代对象。(例如,包含两个元素的数组,如 [[ 1, 'one' ],[ 2, 'two' ]]。)每个键值对都被添加到新的 Map 中。 示例 创建一个新的 Map

const myMap = new Map([
  [1, "one"],
  [2, "two"],
  [3, "three"],
]);
键的相等

键的比较基于零值相等算法。(它曾经使用同值相等,将 0 和 -0 视为不同。检查浏览器兼容性。)这意味着 NaN 是与 NaN 相等的(虽然 NaN !== NaN),剩下所有其他的值是根据 === 运算符的结果判断是否相等。

Object与map的区别 查看MDN吧

设置对象属性

设置对象属性同样适用于 Map 对象,但容易造成困扰。

即,以下的代码能够正常运行(==但不推荐==):

const wrongMap = new Map();
wrongMap["bla"] = "blaa";
wrongMap["bla2"] = "blaaa2";

console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }

但这种设置属性的方式不会改变 Map 的数据结构。它使用的是通用对象的特性。'bla' 的值未被存储在 Map 中,无法被查询到。其他的对这一数据的操作也会==失败==:

wrongMap.has("bla"); // false
wrongMap.delete("bla"); // false
console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }

正确的存储数据到 Map 中的方式是使用 set(key, value) 方法。

const contacts = new Map();
contacts.set("Jessie", { phone: "213-555-1234", address: "123 N 1st Ave" });
contacts.has("Jessie"); // true
contacts.get("Hilary"); // undefined
contacts.set("Hilary", { phone: "617-555-4321", address: "321 S 2nd St" });
contacts.get("Jessie"); // {phone: "213-555-1234", address: "123 N 1st Ave"}
contacts.delete("Raymond"); // false
contacts.delete("Jessie"); // true
console.log(contacts.size); // 1
实例属性(函数)
  1. Map.size() 返回长度
const map1 = new Map();

map1.set('a', 'alpha');
map1.set('b', 'beta');
map1.set('g', 'gamma');

console.log(map1.size);
// Expected output: 3
  1. Map.clear() 清空Map
const map1 = new Map();

map1.set('bar', 'baz');
map1.set(1, 'foo');

console.log(map1.size);
// Expected output: 2

map1.clear();

console.log(map1.size);
// Expected output: 0
  1. Map.delete(key) 删除键值对 传入参数为key 移除 Map 对象中指定的键值对,如果键值对存在并成功被移除,返回 true,否则返回 false。调用 delete 后再调用 map.has(key) 将返回 false
const map1 = new Map();
map1.set('bar', 'foo');

console.log(map1.delete('bar'));
// Expected result: true
// True indicates successful removal

console.log(map1.has('bar'));
// Expected result: false
  1. Map.entries() 创建迭代器 无参数 返回一个新的迭代器对象,其包含 Map 对象中所有键值对 [key, value] 二元数组,以插入顺序排列。

    实际应用:

    • 当你需要同时访问 Map 的键和值时,entries() 方法非常有用。
    • 它常用于遍历 Map 对象,特别是在 for...of 循环中。 注意事项:
    • entries() 方法返回的是一个迭代器,而不是一个数组。
    • 如果你需要一个真正的数组,可以使用 Array.from() 或展开运算符 ... 来转换。 替代方法:
    • Map 对象本身就是可迭代的,所以你也可以直接对 Map 使用 for...of 循环,得到的结果与使用 entries() 相同。
const myMap = new Map();
myMap.set("0", "foo");
myMap.set(1, "bar");
myMap.set({}, "baz");

const mapIter = myMap.entries();

console.log(mapIter.next().value); // ["0", "foo"]
console.log(mapIter.next().value); // [1, "bar"]
console.log(mapIter.next().value); // [Object, "baz"]
// 创建一个 Map
const fruits = new Map();

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

// 使用 entries() 方法
const entriesIterator = fruits.entries();

// 遍历迭代器
for (let entry of entriesIterator) {
  console.log(entry);
}
/*
['apple', 5]
['banana', 3]
['orange', 2]
*/
  1. Map.forEach() 遍历 以插入顺序为 Map 对象中的每个键值对调用一次 callbackFn。如果为 forEach 提供了 thisArg 参数,则它将作为每一次 callback 的 this 值。
function logMapElements(value, key, map) {
  console.log(`m[${key}] = ${value}`);
}

new Map([
  ['foo', 3],
  ['bar', {}],
  ['baz', undefined],
]).forEach(logMapElements);

// Expected output: "m[foo] = 3"
// Expected output: "m[bar] = [object Object]"
// Expected output: "m[baz] = undefined"

  1. Map.get() 获取 返回与指定的键 key 关联的值,若不存在关联的值,则返回 undefined
const map1 = new Map();
map1.set('bar', 'foo');

console.log(map1.get('bar'));
// Expected output: "foo"

console.log(map1.get('baz'));
// Expected output: undefined
  1. Map.has() 查找是否存在 返回一个布尔值,用来表明 Map 对象中是否存在与指定的键 key 关联的值。
const map1 = new Map();
map1.set('bar', 'foo');

console.log(map1.has('bar'));
// Expected output: true

console.log(map1.has('baz'));
// Expected output: false
  1. Map.keys() 返回一个只包含键的迭代器 返回一个新的迭代器对象,其包含 Map 对象中所有元素的键,以插入顺序排列。
// 创建一个新的 Map
const fruits = new Map();

// 添加一些键值对
fruits.set('apple', 'red');
fruits.set('banana', 'yellow');
fruits.set('grape', 'purple');

// 使用 keys() 方法获取键的迭代器
const keysIterator = fruits.keys();

console.log('Keys iterator:', keysIterator);

// 遍历键
for (const key of keysIterator) {
  console.log(key);
}

// 将迭代器转换为数组
const keysArray = Array.from(fruits.keys());
console.log('Keys array:', keysArray);
/*
`Keys iterator: [Map Iterator] { 'apple', 'banana', 'grape' }`

`apple banana grape Keys array: [ 'apple', 'banana', 'grape' ]`
*/

注意与entries() 的区别

  1. 返回的数据:

    • keys() 只返回键。
    • entries() 返回键值对。
  2. 使用场景:

    • 如果你只需要处理 Map 的键,使用 keys() 更加简洁和高效。
    • 如果你需要同时处理键和值,entries() 更适合。
  3. 遍历结果:

    • keys() 遍历结果是单个值。
    • entries() 遍历结果是一个包含两个元素的数组。
  4. 转换为数组:

    • Array.from(map.keys()) 会得到一个只包含键的数组。
    • Array.from(map.entries()) 会得到一个包含键值对数组的数组。
  5. Map.set() 插入新元素 在 Map 对象中设置与指定的键 key 关联的值,并返回 Map 对象。

const map1 = new Map();
map1.set('bar', 'foo');

console.log(map1.get('bar'));
// Expected output: "foo"

console.log(map1.get('baz'));
// Expected output: undefined

// 链式添加元素
myMap.set("bar", "foo").set(1, "foobar").set(2, "baz");

  1. Map.values() 返回一个只包含value的迭代器 返回一个新的迭代对象,其中包含 Map 对象中所有的值,并以插入 Map 对象的顺序排列。 和keys相对
const myMap = new Map();
myMap.set("0", "foo");
myMap.set(1, "bar");
myMap.set({}, "baz");

const mapIter = myMap.values();

console.log(mapIter.next().value); // "foo"
console.log(mapIter.next().value); // "bar"
console.log(mapIter.next().value); // "baz"

  1. Map[Symbol.iterator]() 创建数组 返回一个新的迭代器对象,其包含 Map 对象中所有元素 [key, value] 二元数组,以插入顺序排列。
const map1 = new Map();

map1.set('0', 'foo');
map1.set(1, 'bar');

const iterator1 = map1[Symbol.iterator]();

for (const item of iterator1) {
  console.log(item);
}
// Expected output: Array ["0", "foo"]
// Expected output: Array [1, "bar"]

示例
  1. 使用Map对象

  2. 将NaN作为Map的键

  3. 使用for...of迭代Map

  4. 使用forEach() 迭代Map

  5. Map与数组对象的关系

  6. 复制或合并Maps Map 能像数组一样被复制:

const original = new Map([[1, "one"]]);

const clone = new Map(original);

console.log(clone.get(1)); // one
console.log(original === clone); // false. 浅比较 不为同一个对象的引用

**备注:**请记住,_数据本身_未被克隆。

Map 对象间可以进行合并,但是会保持键的唯一性。

const first = new Map([
  [1, "one"],
  [2, "two"],
  [3, "three"],
]);

const second = new Map([
  [1, "uno"],
  [2, "dos"],
]);

// 合并两个 Map 对象时,如果有重复的键值,则后面的会覆盖前面的。
// 展开语法本质上是将 Map 对象转换成数组。
const merged = new Map([...first, ...second]);

console.log(merged.get(1)); // uno
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three

Map 对象也能与数组合并:

const first = new Map([
  [1, "one"],
  [2, "two"],
  [3, "three"],
]);

const second = new Map([
  [1, "uno"],
  [2, "dos"],
]);

// Map 对象同数组进行合并时,如果有重复的键值,则后面的会覆盖前面的。
const merged = new Map([...first, ...second, [1, "eins"]]);

console.log(merged.get(1)); // eins
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three