6-JS-数据类型-Map 和 Set (映射和集合)

Map 和 Set (映射和集合) Map 是一个带键的数据项的集合,就像一个对象一样。但是它们直接最大的差别是 Map 允许任何类型的键。
方法和属性如下:

  • new Map() —— 创建 map。
  • map.set(key, value) —— 根据键存储值。
  • map.get(key) —— 根据键来返回值,如果 map 中不存在对应的 key,则返回 undefined
  • map.has(key) —— 如果 key 存在则返回 true,否则返回 false
  • map.delete(key) —— 删除指定键的值。
  • map.clear() —— 清空 map。
  • map.size —— 返回当前元素个数。
举例
let map = new Map(); map.set('1', 'str1'); // 字符串键 map.set(1, 'num1'); // 数字键 map.set(true, 'bool1'); // 布尔值键// 还记得普通的 Object 吗? 它会将键转化为字符串 // Map 则会保留键的类型,所以下面这两个结果不同: alert( map.get(1)); // 'num1' alert( map.get('1') ); // 'str1'alert( map.size ); // 3

Map 可以使用对象作为键。
let john = { name: "John" }; // 存储每个用户的来访次数 let visitsCountMap = new Map(); // john 是 Map 中的键 visitsCountMap.set(john, 123); alert( visitsCountMap.get(john) ); // 123

使用对象作为键是 Map 最值得注意和重要的功能之一。对于字符串键,Object(普通对象)也能正常使用,但对于对象键则不行。
尝试一下
let john = { name: "John" }; let visitsCountObj = {}; // 尝试使用对象visitsCountObj[john] = 123; // 尝试将 john 对象作为键// 是写成了这样! alert( visitsCountObj["[object Object]"] ); // 123

注意:链式调用。每一次 map.set 调用都会返回 map 本身,所以我们可以进行链式调用
map.set('1', 'str1') .set(1, 'num1') .set(true, 'bool1');

Map 迭代
  • map.keys() —— 遍历并返回所有的键(returns an iterable for keys),
  • map.values() —— 遍历并返回所有的值(returns an iterable for values),
  • map.entries() —— 遍历并返回所有的实体(returns an iterable for entries)[key, value]for..of 在默认情况下使用的就是这个。
举例:
let recipeMap = new Map([ ['cucumber', 500], ['tomatoes', 350], ['onion',50] ]); // 遍历所有的键(vegetables) for (let vegetable of recipeMap.keys()) { alert(vegetable); // cucumber, tomatoes, onion }// 遍历所有的值(amounts) for (let amount of recipeMap.values()) { alert(amount); // 500, 350, 50 }// 遍历所有的实体 [key, value] for (let entry of recipeMap) { // 与 recipeMap.entries() 相同 alert(entry); // cucumber,500 (and so on) }

使用插入顺序
迭代的顺序与插入值的顺序相同。与普通的 Object 不同,Map 保留了此顺序。
而且 Map 有内置的 forEach 方法,和 Array 类似
// 对每个键值对 (key, value) 运行 forEach 函数 recipeMap.forEach( (value, key, map) => { alert(`${key}: ${value}`); // cucumber: 500 etc });

Object.entries: 从对象创建Map 当创建一个 Map 后,我们可以传入一个带有键值对的数组来继续初始化。
// 键值对 [key, value] 数组 let map = new Map([ ['1','str1'], [1,'num1'], [true, 'bool1'] ]); alert( map.get('1') ); // str1

【6-JS-数据类型-Map 和 Set (映射和集合)】也可以
let obj = { name: "John", age: 30 }; let map = new Map(Object.entries(obj)); alert( map.get('name') ); // John

这里,Object.entries 返回键/值对数组:[ ["name","John"], ["age", 30] ]。这就是 Map 所需要的格式。
Object.fromEntries:从 Map 创建对象 Object.fromEntries 方法的作用是相反的:给定一个具有 [key, value] 键值对的数组,它会根据给定数组创建一个对象:
let prices = Object.fromEntries([ ['banana', 1], ['orange', 2], ['meat', 4] ]); // 现在 prices = { banana: 1, orange: 2, meat: 4 }alert(prices.orange); // 2

我们可以使用 Object.fromEntriesMap 得到一个普通对象(plain object)。
如果我们在 Map 中存储了一些数据,但是我们需要把这些数据传给需要普通对象的第三方代码。
let map = new Map(); map.set('banana', 1); map.set('orange', 2); map.set('meat', 4); let obj = Object.fromEntries(map.entries()); // 创建一个普通对象(plain object)(*)// 完成了! // obj = { banana: 1, orange: 2, meat: 4 }alert(obj.orange); // 2

Set 是一个特殊的类型集合,没有键,它的每一个值只能出现一次。
主要方法如下:
  • new Set(iterable) —— 创建一个 set,如果提供了一个 iterable 对象(通常是数组),将会从数组里面复制值到 set 中。
  • set.add(value) —— 添加一个值,返回 set 本身
  • set.delete(value) —— 删除值,如果 value 在这个方法调用的时候存在则返回 true ,否则返回 false
  • set.has(value) —— 如果 value 在 set 中,返回 true,否则返回 false
  • set.clear() —— 清空 set。
  • set.size —— 返回元素个数。
它的主要特点是,重复使用同一个值调用 set.add(value) 并不会发生什么改变。这就是 Set 里面的每一个值只出现一次的原因。
例如,我们有客人来访,我们想记住他们每一个人。但是已经来访过的客人再次来访,不应造成重复记录。每个访客必须只被“计数”一次。
Set 可以帮助我们解决这个问题:
let set = new Set(); let john = { name: "John" }; let pete = { name: "Pete" }; let mary = { name: "Mary" }; // visits,一些访客来访好几次 set.add(john); set.add(pete); set.add(mary); set.add(john); set.add(mary); // set 只保留不重复的值 alert( set.size ); // 3for (let user of set) { alert(user.name); // John(然后 Pete 和 Mary) }

Set 的替代方法可以是一个用户数组,用 arr.find 在每次插入值时检查是否重复。但是这样性能会很差,因为这个方法会遍历整个数组来检查每个元素。Set 内部对唯一性检查进行了更好的优化。
Set 迭代(iteration) 使用 for…of 或 forEach 来遍历 Set
let set = new Set(["oranges", "apples", "bananas"]); for (let value of set) alert(value); // 与 forEach 相同: set.forEach((value, valueAgain, set) => { alert(value); });

forEach 的回调函数有三个参数:一个 value,然后是 同一个值 valueAgain,最后是目标对象。没错,同一个值在参数里出现了两次。
forEach 的回调函数有三个参数,是为了与 Map 兼容。当然,这看起来确实有些奇怪。但是这对在特定情况下轻松地用 Set 代替 Map 很有帮助,反之亦然。
Map 中用于迭代的方法在 Set 中也同样支持:
  • set.keys() —— 遍历并返回所有的值(returns an iterable object for values),
  • set.values() —— 与 set.keys() 作用相同,这是为了兼容 Map
  • set.entries() —— 遍历并返回所有的实体(returns an iterable object for entries)[value, value],它的存在也是为了兼容 Map

    推荐阅读