ES6 Set 和 Map数据结构

404 阅读5分钟

「这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战

ES6 Set 和 Map 数据结构

一、简介

ES6标准提供了两个新的数据结构,分别是 SetMap ,也可以叫做 集合字典,与之相对应的还有 weakSet 和 weakMap。 这篇文章主要是介绍一下这两种数据结构。

二、Set

定义

Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。

特点:

  1. Set 对象是值的集合
  2. 值可以是任何类型
  3. Set 中的元素是唯一

属性和方法

// 构造函数,默认就是Set函数
Set.prototype.constructor
// 返回Set实例的成员总数
Set.prototype.size

// Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。

// 操作方法

// 添加某个值,返回 Set 结构本身
Set.prototype.add(value)

// 删除某个值,返回一个布尔值,表示删除是否成功
Set.prototype.delete(value)

// 返回一个布尔值,表示该值是否为Set的成员
Set.prototype.has(value)

// 清除所有成员,没有返回值
Set.prototype.clear()

// 遍历方法

// 返回键名的遍历器
Set.prototype.keys()

// 返回键值的遍历器
Set.prototype.values()

// 返回键值对的遍历器
Set.prototype.entries()

// 使用回调函数遍历每个成员
Set.prototype.forEach()

特性

  1. Set的构造函数的参数必须是一个可迭代对象,或者为空(null,undefined)

参数可以是数组,类数组,字符串,或其他部署了 Iterator 接口的数据类型

image.png

  1. Set 元素的唯一性,这里就涉及到怎么判别相等。
  • +0 (+0 严格相等于-0)和-0是不同的值,但是在Set中会被合并成 0
  • 多个NaN,undefined,null会被合并成一个
  • 多个undefined会被合并成一个
  • 多个{}会不被合并

image.png

  1. Set.prototype.add(value) 返回值为 Set 结构本身,所以可以链式调用。

image.png

  1. Set 和 数组 的互换
// Array => Set
// new Set(arr)
let set = new Set([1,2,3])

// Set => Array
// Array.from(set)
// [...Set]
let arr = Array.from(set)
let arr = [...set]

image.png

  1. 数组去重(Set版)
let x = [...new Set(numbers)]
  1. Set的size属性可写,但不会生效。

这一点要和数组的length区分开

image.png

三、Map

定义

Map 数据结构,类似于对象,也是键值对的集合,但是“键”的范围不限于字符串和Symbol,各种类型的值(包括对象)都可以当作键。Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。

特点:

  1. 保存键值对
  2. 提供“值—值”对应
  3. 遍历时按照键的原始插入顺序

属性和方法

// `size` 是可访问属性,用于返回 一个 Map 对象的成员数量
Map.prototype.size

// 添加或更新一个指定了键(`key`)和值(`value`)的(新)键值对,返回值为 Map 对象
Map.prototype.set(key, value)

// 返回一个 `Map` 对象中与指定键相关联的值,如果找不到这个键则返回 `undefined`
Map.prototype.get(key)

// 如果 `Map` 对象中存在该元素,则移除它并返回 *`true`*;否则如果该元素不存在则返回 `false`
Map.prototype.delete(key)

// 返回一个bool值,用来表明map 中是否存在指定元素
Map.prototype.has(key)

// 移除Map对象中的所有元素,返回 undefined
Map.prototype.clear()

// 返回一个引用的 Iterator 对象。它包含按照顺序插入 `Map` 对象中每个元素的key值
Map.prototype.keys()

// 返回一个新的 Iterator 对象。它包含按顺序插入Map对象中每个元素的value值。
Map.prototype.values()

// 返回一个新的包含 `[key, value]` 对的 `Iterator` 对象,返回的迭代器的迭代顺序与 `Map` 对象的插入顺序相同
Map.prototype.entries()

// 按照插入顺序依次对 `Map` 中每个键/值对执行一次给定的函数
Map.prototype.forEach()

特性

  1. Objects 和 maps 的比较
比较MapObject
意外的键Map 默认情况不包含任何键。只包含显式插入的键。一个 Object 有一个原型, 原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。注意:  虽然 ES5 开始可以用 Object.create(null) 来创建一个没有原型的对象,但是这种用法不太常见。
键的类型一个 Map的键可以是任意值,包括函数、对象或任意基本类型。一个Object 的键必须是一个 String 或是Symbol
键的顺序Map 中的 key 是有序的。因此,当迭代的时候,一个 Map 对象以插入的顺序返回键值。一个 Object 的键是无序的注意:自ECMAScript 2015规范以来,对象确实保留了字符串和Symbol键的创建顺序; 因此,在只有字符串键的对象上进行迭代将按插入顺序产生键。
Size Map 的键值对个数可以轻易地通过size 属性获取Object 的键值对个数只能手动计算
迭代Map 是 iterable 的,所以可以直接被迭代。迭代一个Object需要以某种方式获取它的键然后才能迭代。
性能在频繁增删键值对的场景下表现更好。在频繁添加和删除键值对的场景下未作出优化。
  1. Map中键的相等(Key equality)
  • 键的比较是基于 sameValueZero 算法:
  • NaN 是与 NaN 相等的(虽然 NaN !== NaN),剩下所有其它的值是根据 === 运算符的结果判断是否相等。
  • 在目前的ECMAScript规范中,-0+0被认为是相等的。
  1. Map的size属性可写,但不会生效。

这一点要和数组的length区分开

image.png

  1. Map 与 数组 的关系 二维键值对数组可以和Map对象相互转换
let kvArray = [["key1", "value1"], ["key2", "value2"]];

// 二维键值对数组 => Map对象
let myMap = new Map(kvArray);

// Map对象 => 二维键值对数组
// 使用Array.from函数,展开运算符

let x0 = Array.from(myMap);

let x1 = [...myMap];

// 或者在键或者值的迭代器上使用Array.from,进而得到只含有键或者值的数组
console.log(Array.from(myMap.keys())); // 输出 ["key1", "key2"]

  1. Map对象的合并 new Map([...first, ...second])
    如果有重复的键值,则后面的会覆盖前面的
let first = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]);

let second = new Map([
  [1, 'uno'],
  [2, 'dos']
]);

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