「这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战」
ES6 Set 和 Map 数据结构
一、简介
ES6标准提供了两个新的数据结构,分别是 Set 和 Map ,也可以叫做 集合 和 字典,与之相对应的还有 weakSet 和 weakMap。 这篇文章主要是介绍一下这两种数据结构。
二、Set
定义
Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
特点:
- Set 对象是
值的集合 - 值可以是
任何类型 - 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()
特性
- Set的构造函数的参数必须是一个可迭代对象,或者为空(null,undefined)
参数可以是数组,类数组,字符串,或其他部署了 Iterator 接口的数据类型
- Set 元素的唯一性,这里就涉及到怎么判别相等。
- +0 (+0 严格相等于-0)和-0是不同的值,但是在Set中会被合并成 0
- 多个NaN,undefined,null会被合并成一个
- 多个undefined会被合并成一个
- 多个{}会不被合并
- Set.prototype.add(value) 返回值为 Set 结构本身,所以可以链式调用。
- 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]
- 数组去重(Set版)
let x = [...new Set(numbers)]
- Set的size属性可写,但不会生效。
这一点要和数组的length区分开
三、Map
定义
Map 数据结构,类似于对象,也是键值对的集合,但是“键”的范围不限于字符串和Symbol,各种类型的值(包括对象)都可以当作键。Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。
特点:
- 保存键值对
- 提供“值—值”对应
- 遍历时按照键的原始插入顺序
属性和方法
// `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()
特性
| 比较 | Map | Object |
|---|---|---|
| 意外的键 | Map 默认情况不包含任何键。只包含显式插入的键。 | 一个 Object 有一个原型, 原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。注意: 虽然 ES5 开始可以用 Object.create(null) 来创建一个没有原型的对象,但是这种用法不太常见。 |
| 键的类型 | 一个 Map的键可以是任意值,包括函数、对象或任意基本类型。 | 一个Object 的键必须是一个 String 或是Symbol。 |
| 键的顺序 | Map 中的 key 是有序的。因此,当迭代的时候,一个 Map 对象以插入的顺序返回键值。 | 一个 Object 的键是无序的注意:自ECMAScript 2015规范以来,对象确实保留了字符串和Symbol键的创建顺序; 因此,在只有字符串键的对象上进行迭代将按插入顺序产生键。 |
| Size | Map 的键值对个数可以轻易地通过size 属性获取 | Object 的键值对个数只能手动计算 |
| 迭代 | Map 是 iterable 的,所以可以直接被迭代。 | 迭代一个Object需要以某种方式获取它的键然后才能迭代。 |
| 性能 | 在频繁增删键值对的场景下表现更好。 | 在频繁添加和删除键值对的场景下未作出优化。 |
- 键的比较是基于
sameValueZero算法: NaN是与NaN相等的(虽然NaN !== NaN),剩下所有其它的值是根据===运算符的结果判断是否相等。- 在目前的ECMAScript规范中,
-0和+0被认为是相等的。
- Map的size属性可写,但不会生效。
这一点要和数组的length区分开
- 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"]
- 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]);
- Map对象的复制