ES6中Map


Map的mdn文档地址

一、描述

1、概念及特性

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

let mapObj=new Map()
mapObj.set('theFirstObj','theFirstValue');
mapObj.set('theSecondObj','theSecondValue');
mapObj.set('theThirdObj','theThirdValue');
for(var i of mapObj){
    console.log(i)//返回一个形式为 [key, value] 的数组
}

在这里插入图片描述

2、键的相等

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

2.1 关于零值相等:

类似于同值相等,但+0 和 -0被视为相等。
NaN 视作是相等的
零值相等不作为 JavaScript API 公开,但可以通过自定义代码实现:


function sameValueZero(x, y) {
  if (typeof x === "number" && typeof y === "number") {
    // x 和 y 相等(可能是 -0 和 0)或它们都是 NaN
    return x === y || (x !== x && y !== y);
  }
  return x === y;
}
console.log(sameValueZero(NaN,NaN),NaN==NaN,NaN===NaN)

在这里插入图片描述

零值相等在搜索期间通常具有最实用的行为,特别是在与 NaN 一起使用时。它被用于 Array.prototype.includes()、TypedArray.prototype.includes() 及 Map 和 Set 方法用来比较键的相等性

3、Object 和 Map 的比较

Object Map 类似的是,它们都允许你按键存取一个值、删除键、检测一个键是否绑定了值。

3.1关于默认键/意外的键

  • Map 默认不包含任何键。它只包含显式存入的键值对。
  • Object 有原型,因此它包含默认的键,如果不小心的话,它们可能会与你自己的键相冲突。(备注:这可以通过使用 Object.create(null) 来绕过,但很少这样做。)

3.2键的类型

  • Map 的键可以为 任意值(包括函数、对象或任何原始值)。
  • Object 的键必须为 StringSymbol

3.3键的顺序

  • Map 中的键以简单、直接的方式排序(FIFO 原则):Map 对象按照插入的顺序迭代条目、键和值。
  • Object不像Map那样有固定的顺序

尽管现在普通的 Object 的键是有序的,但情况并非总是如此,并且其排序比较复杂的。
因此,最好不要依赖属性的顺序。
该顺序最初仅在 ECMAScript 2015 中为自有属性定义;ECMAScript 2020 还定义了继承属性的顺序。
但请注意,没有单一机制可以迭代对象的所有属性;各种机制各自包含不同的属性子集。

for-in 仅包含可枚举的字符串键属性;
Object.keys 仅包含可枚举的自有字符串键属性;
Object.getOwnPropertyNames 包括自有的字符串键属性,即使是不可枚举的;
Object.getOwnPropertySymbols 仅对 Symbol 键属性执行相同的操作,等等。)

3.4安全性

  • Map 可以安全地与用户提供的键值一起使用。
  • Object 上设置用户提供的键值对可能会允许攻击者覆盖对象的原型,这可能会导致对象注入攻击。就像意外的键问题一样,这也可以通过使用 null 原型对象来缓解

3.5大小

  • Map 中的项目数量很容易从其 size 属性中获得。
  • 确定 Object 中的项目数量通常更麻烦,效率也较低。一种常见的方法是通过获取 Object.keys() 返回的数组的长度。

3.6迭代

  • Map 是可迭代对象,所以它可以直接迭代。
  • Object 没有实现迭代协议,因此对象默认情况下不能直接通过 JavaScript 的 for...of 语句进行迭代。

一个对象可以实现迭代协议,或者你可以使用 Object.keys Object.entries 来获取一个对象的可迭代对象。
for...in 语句允许迭代对象的可枚举属性。

3.7性能

  • Map在涉及频繁添加和删除键值对的场景中表现更好。
  • Object未针对频繁添加和删除键值对进行优化。

3.8序列化和解析

  • Map没有对序列化或解析的原生支持。
    (可以通过使用 JSON.stringify() 及其 replacer 参数和 JSON.parse() 及其 reviver 参数来为 Map 构建自己的序列化和解析支持。参见 Stack Overflow 问题 How do you JSON.stringify an ES6 Map?)。
  • Object原生支持使用 JSON.stringify() 序列化 Object JSON以及使用 JSON.parse() 解析 JSON Object

4、设置对象属性

4.1正确

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


const contacts = new Map();

contacts.set("Jessie", { phone: "213-555-1234", address: "123 N 1st Ave" });
console.log('contacts.has("Jessie")=>'+contacts.has("Jessie")); // true

console.log('contacts.get("Hilary")=>'+contacts.get("Hilary")); // undefined
contacts.set("Hilary", { phone: "617-555-4321", address: "321 S 2nd St" });

console.log('contacts.get("Jessie")=>'+contacts.get("Jessie")); // {phone: "213-555-1234", address: "123 N 1st Ave"}
console.log('contacts.delete("Raymond")=>'+contacts.delete("Raymond")); // false
console.log('contacts.delete("Jessie")=>'+contacts.delete("Jessie")); // true

console.log(contacts.size); // 1

在这里插入图片描述

4.2能运行但不推荐


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() 构造函数创建 Map 对象。

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

1、语法


new Map()
new Map(iterable)

2、参数

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

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

三、静态属性及方法

1、属性

Map[@@species]

  • 用于创建派生对象的构造函数。
  • Map[@@species] 静态访问器属性是一个未使用的访问器属性,指定了如何复制 Map 对象。

备注: 目前所有 Map 方法均未使用此属性。

1.1 语法


Map[Symbol.species]

1.2 返回值

  • 调用 get @@species 的构造函数的值(this)。返回值用于构造复制的 Map 实例。

2、方法

2.1 groupBy()

  • Map.groupBy() 静态方法使用提供的回调函数返回的值对给定可迭代对象中的元素进行分组。最终返回的 Map 使用测试函数返回的唯一值作为键,可用于获取每个组中的元素组成的数组。
  • 该方法主要用于对与对象相关的元素进行分组,特别是当该对象可能随时间而变化时。如果对象不变,可以使用字符串表示它,并使用 Object.groupBy() 分组元素。

备注: 在某些浏览器的某些版本中,此方法被实现为 Array.prototype.groupToMap() 方法。
由于 web 兼容性问题,它现在以静态方法实现。
在这里插入图片描述

2.1.1 语法


  groupBy(items, callbackFn)

2.1.2 参数

  • items:一个将进行元素分组的可迭代对象(例如 Array)。

  • callbackFn:对可迭代对象中的每个元素执行的函数。它应该返回一个值(对象或原始类型)来表示当前元素的分组。该函数被调用时将传入以下参数:

  • element:数组中当前正在处理的元素。
  • index:正在处理的元素在数组中的索引。

2.1.3 返回值

  • 一个包含了每一个组的键的 Map 对象,每个键都分配了一个包含关联组元素的数组。

2.1.4 描述

Map.groupBy() 为可迭代对象中的每个元素调用一次提供的 callbackFn 函数。
该回调函数应返回一个值,用以表示相关元素的分组。
callbackFn 返回的值会被用作 Map.groupBy() 返回的 Map 的键。
每个键都有一个相关联的数组,其中包含回调函数返回相同值的所有元素

返回的Map中的元素和原始可迭代对象中的元素相同 (不是深拷贝)
更改元素的内部结构将反映在原始可迭代对象和返回的 Map 中。

当你需要分组与特定对象相关的信息时,此方法非常有用。
因为即使对象被修改,它仍将作为返回的 Map 的键继续工作。
如果你改为为对象创建字符串表示形式,并在 Object.groupBy() 中将其用作分组键,则必须在对象改变时维护原始对象和其表示之间的映射。

备注: 要访问返回的 Map 中的分组,必须使用最初用作 Map 键的相同对象(尽管可以修改其属性)。不能使用另一个恰好具有相同名称和属性的对象。

2.1.5 示例

  • 使用 Map.groupBy()

首先,定义一个包含代表各种食品库存的对象的数组。每种食品都有一个 type 和一个 quantity 属性。


const inventory = [
  { name: "芦笋", type: "蔬菜", quantity: 9 },
  { name: "香蕉", type: "水果", quantity: 5 },
  { name: "山羊", type: "肉", quantity: 23 },
  { name: "樱桃", type: "水果", quantity: 12 },
  { name: "鱼", type: "肉", quantity: 22 },
];

下面的代码使用箭头函数(函数返回名为 restocksufficient 的对象键,具体取决于元素是否满足 quantity < 6)来调用 Map.groupBy()。返回的 result 对象是一个 Map,因此我们需要使用键调用 get() 来获取数组。


const restock = { restock: true };
const sufficient = { restock: false };
const result = Map.groupBy(inventory, ({ quantity }) =>
  quantity < 6 ? restock : sufficient,
);
console.log(result)
console.log(result.get(restock));
// [{ name: "香蕉", type: "水果", quantity: 5 }]

请注意,函数参数{ quantity }是函数参数的对象解构语法的一个基本示例。
这会解构作为参数传递的对象的 quantity 属性,并将其赋值给函数体中名为 quantity 的变量。这是一种非常简洁的访问函数中相关元素的值的方式。

Map 的键在修改后仍可以使用。但是,不能重新创建键并仍然使用它。
因此,任何需要使用映射的内容都保留对其键的引用是非常重要的。


// 键被修改后仍可以使用
restock["fast"] = true;
console.log(result.get(restock));
// [{ name: "香蕉", type: "水果", quantity: 5 }]

// 不能使用新的键,即使它具有相同的结构!
const restock2 = { restock: true };
console.log('result.get(restock2):'+result.get(restock2)); // undefined

在这里插入图片描述

四、实例属性及方法

1、属性

1.1 Map.prototype.constructor

  • 创建实例对象的构造函数。对于 Map 实例,初始值为 Map 构造函数。

1.2 Map.prototype.size

  • 返回 Map 对象中的键值对数量。

1.3 Map.prototype[@@toStringTag]

  • @@toStringTag 属性的初始值是字符串 “Map”。该属性在 Object.prototype.toString() 中使用。

2、方法

2.1 set()

  • 设置对象属性
2.1.1 语法
   set(key, value)
2.1.2 参数
  • key:要添加到 Map 对象的元素的键。
  • value:要添加到 Map 对象的元素的值。
  • key/value可以是任何 JavaScript 类型(任何原始值或任何类型的 JavaScript 对象)。
2.1.3 返回值是Map对象
const myMap = new Map();

// 将一个新元素添加到 Map 对象
myMap.set("bar", "foo");
myMap.set(1, "foobar");
console.log(myMap,'将一个新元素添加到 Map 对象')
// 在 Map 对象中更新某个元素的值
myMap.set("bar", "baz");
console.log(myMap,'在 Map 对象中更新某个元素的值')

在这里插入图片描述

2.1.4 链式使用
  • 因为 set() 方法返回 Map 对象本身,所以你可以像下面这样链式调用它:
const myMap = new Map();
// 链式添加元素
myMap.set("bar", "foo").set(1, "foobar").set(2, "baz");

在这里插入图片描述

2.2 get()

  • Map 实例的 get() 方法返回该 map 中的指定元素。如果与所提供的键相关联的值是一个对象,那么你将获得该对象的引用,对该对象所做的任何更改都会有效地在 Map 对象中修改它。
2.2.1 语法
  get(key)
2.2.2 参数
  • key:从 Map 对象返回的元素的键。
2.2.3 返回值
  • 与指定键相关联的元素,如果键在 Map 对象中找不到,则返回 undefined

示例1:使用get()


const myMap = new Map();
myMap.set("bar", "foo");

console.log(myMap.get("bar")); // 返回 "foo"
console.log(myMap.get("baz")); // 返回 undefined

在这里插入图片描述

示例2:使用 get() 获取对对象的引用

const arr = [];
const myMap = new Map();
myMap.set("bar", arr);

myMap.get("bar").push("foo");

console.log(arr); // ["foo"]
console.log(myMap.get("bar")); // ["foo"]

在这里插入图片描述

注意,持有原始对象引用的映射实际上意味着对象不能被垃圾回收,这可能会导致意外的内存问题。如果希望存储在 map 中的对象具有与原始对象相同的生命周期,请考虑使用 WeakMap
WeakMap文档

2.3 delete()

  • 从该 Map 中删除指定键的元素。
2.3.1 语法
  delete(key)
2.3.2 参数
  • key:要从 Map 对象中删除的元素的键。
2.3.3 返回值
  • 如果 Map 对象中的元素存在并已被移除,则为 true
  • 如果该元素不存在,则为 false

const myMap = new Map();
myMap.set("bar", "foo");

console.log(myMap.delete("bar")); // 返回 true。成功地移除元素
console.log(myMap.has("bar")); // 返回 false。"bar" 元素将不再存在于 Map 实例中

在这里插入图片描述

2.4 clear()

  • 移除 Map 对象中所有的键值对。
2.4.1 语法
  clear()

const map1 = new Map();

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

console.log(map1.size);// 2

map1.clear();

console.log(map1.size);//  0

在这里插入图片描述

2.5 has()

  • Map 实例的 has() 方法返回一个布尔值,指示具有指定键的元素是否存在。
2.5.1 语法
   has(key)
2.5.2 参数
  • key:用于测试 Map 对象中是否存在的元素的键。
2.5.3 返回值
  • 如果 Map 对象中存在具有指定键的元素,则返回 true;否则返回 false

const myMap = new Map();
myMap.set("bar", "foo");

console.log(myMap.has("bar")); // true
console.log(myMap.has("baz")); // false

在这里插入图片描述

2.5.4 与get()区别

get()返回的是一个与指定键相关联的元素has()返回的是布尔值

2.6 keys()

  • Map 实例的 keys() 方法返回一个新的 map 迭代器 (en-US)对象,该对象包含了此 Map 中每个元素的键,按插入顺序排列。
2.6.1 语法
  keys()
2.6.2 返回值
const myMap = new Map();
myMap.set("0", "foo");
myMap.set(1, "bar");
myMap.set({}, "baz");

const mapIter = myMap.keys();

console.log(mapIter)

console.log(mapIter.next().value); // "0"
console.log(mapIter.next().value); // 1
console.log(mapIter.next().value); // {}

在这里插入图片描述

2.7 values()

  • values() 方法返回一个新的 map 迭代器 (en-US)对象,该对象包含此 map 中每个元素的值,按插入顺序排列。
2.7.1 语法
  values()
2.7.2 返回值
const myMap = new Map();
myMap.set("0", "foo");
myMap.set(1, "bar");
myMap.set({}, "baz");

const mapIter = myMap.values();

console.log(mapIter)

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

在这里插入图片描述

2.8 entries()

  • Map 实例的entries()方法返回一个新的 map 迭代器 (en-US)对象,该对象包含了此 map 中的 每个元素的 [key, value] 对,按插入顺序排列。
2.8.1 语法
  entries()
2.8.2 返回值

const myMap = new Map();
myMap.set("0", "foo");
myMap.set(1, "bar");
myMap.set({}, "baz");

const mapIter = myMap.entries();

console.log(mapIter)

console.log(mapIter.next().value); // ["0", "foo"]
console.log(mapIter.next().value); // [1, "bar"]
console.log(mapIter.next().value); // [Object, "baz"]

在这里插入图片描述

2.9 forEach()

  • Map 实例的 forEach() 方法按插入顺序对该 map 中的每个键/值对执行一次提供的函数。
2.9.1 语法

  forEach(callbackFn)
  forEach(callbackFn, thisArg)
  
2.9.2 参数
  • callbackFn:为 map 中每个元素执行的函数。使用以下参数调用该函数:
  • value:每个迭代的值。
  • key:每个迭代的键。
  • map:正在迭代的 map。
  • thisArg :非必填,执行 callbackFn 时用作 this 的值。
2.9.3 描述

forEach 方法会对 map 中每个真实存在的键执行一次提供的 callback

  • 不会为被删除的键执行函数
  • 会为存在但值为 undefined 的值执行函数

callbackFn 接收三个参数:

  • 当前的 value
  • 当前的 key
  • 正在被遍历的 Map 对象

如果向 forEach 提供了 thisArg 参数,那么每次 callback 被调用时,其都会被传入以用作 this 的值。
否则,undefined 将会被传入以用作 this 的值。
最终 callback 可观察到的 this 值将会根据确定函数所观察到 this 的常用规则来确定。

  • 每个值只被访问一次,除非它在 forEach 结束前被删除并被重新添加。
  • 对于被访问前就删除的值,callback 不会为其调用。
  • forEach 结束前被新添加的值都将会被访问。
function logMapElements(value, key, map) {
  console.log(`map.get('${key}') = ${value}`);
}
new Map([
  ["foo", 3],
  ["bar", {}],
  ["baz", undefined],
]).forEach(logMapElements);
// 打印:
// "map.get('foo') = 3"
// "map.get('bar') = [object Object]"
// "map.get('baz') = undefined"

在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伊昂呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值