浅谈一下Set和Map
前言
在每次使用ES6新特性的时候,我们都已经非常习惯这些语法,但在近期的使用中,我对Set和Map的概念和应用场景等,出现了一些很模糊的概念,因此打算借此机会复习一下set和map。
Set
Set是ES6新增的数据结构,一种叫做集合的数据结构,它类似于数组,但是成员的值都是唯一的(也就是说不能重复),并且是无序的。
set本身是一个构造函数,可以用来生成set数据结构。可以接受一个数组(或者其他具有iterable 接口的其他数据结构)作为参数,用来初始化。
let set = new Set([1, 2, 3])
console.log(set); // Set(3) { 1, 2, 3 }
set实例的方法和属性
- 属性:
Set.prototype.size:返回Set实例的成员总数。Set.prototype.constructor:构造函数,默认指向Set函数
- 操作方法:
Set.prototype.add(value):添加某个值,返回 Set 结构本身。Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。Set.prototype.clear():清除所有成员,没有返回值。
- 遍历方法
Set.prototype.keys():返回键名的遍历器Set.prototype.values():返回键值的遍历器Set.prototype.entries():返回键值对的遍历器Set.prototype.forEach():使用回调函数遍历每个成员
由于其特性多用于数组去重
[...new Set([1, 1, 2, 2, 3, 4])] // [1, 2, 3, 4]
字符串去除重复字符
[...new Set('aabbccdde')].join('') // 'abcde'
Set数据结构成员唯一性
Set添加成员使用。add()方法,在该方法中会对添加的成员进行校验,判断在实例中是否已经存在该成员,存在就不会添加,反之就添加。其中判断成员的类型也属于不同,判断的标准相当于 ===。
但有一点不同在于:
console.log(NaN === NaN); // false
let set = new Set()
set.add(NaN)
set.add(NaN)
console.log(set.size); // 1
运算符===会判断NaN不等于自身,而Set会判断NaN等于自身。
Set数据结构的遍历
之前说过,Set类型是无序的,因此遍历的顺序就是插入的顺序。
Set数据类型没有键名只有键值,因此实例的key和value是相同的,因此Set.prototype.keys()和Set.prototype.values()返回的结果是一样的。
Set结构的实例默认可以遍历,并且values()方法就是它默认调用的遍历方法。
Set.prototype[Symbol.iterator] === Set.prototype.values
也就是说Set实例可以使用 for of遍历
Set实现交集,并集,差集
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}
// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}
// (a 相对于 b 的)差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}
Map
在JavaScript中对象本质上是key/value结构,而传统key只能使用字符串,这就带来很大限制,因此ES6 提供了 Map 数据结构。
定义
Map数据结构类似于对象,也是key/value结构,但是map的key的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
let map = new Map()
let o = {a: 1}
map.set(o, 'hahah')
console.log(map); // Map(1) { { a: 1 } => 'hahah' }
Map作为构造函数,也可以接受一个数组作为参数。这个数组的成员表示一个个的键值对。任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构都可以当作Map构造函数的参数。
let arr = [
['name', 'birde'],
['sex', 'man'],
]
let map = new Map(arr)
console.log(map); // Map(2) { 'name' => 'birde', 'sex' => 'man' }
// 使用set结构做参数
let set = new Set()
set.add(['name', 'birde'])
let map2 = new Map(set)
console.log(map2); // Map(1) { 'name' => 'birde' }
// 使用map结构做参数
let map3 = new Map(map)
console.log(map3); // Map(2) { 'name' => 'birde', 'sex' => 'man' }
对同一键名多次赋值,后面的会覆盖前面的
因为map可以使用对象来充当key值,但object是引用属性,因此只有对同一个对象的引用才能视为是同一个键。
let a = [2]
map.set([1], 111)
map.set(a, '1231')
console.log(map.get([1])); // undefined
console.log(map.get(a)); // 1231
如果key值为基础属性,那就只要判断key严格相等就可以了,比如+0 和 -0为同一个值,'true' 和 true不是同一个值,和Set判断一样,map也会将NaN判断为同一个key。
map实例的方法和属性
- 属性:
Map.prototype.size:返回Map实例的成员总数。Map.prototype.constructor:构造函数,默认指向Map函数
- 操作方法:
Map.prototype.set(key, value):set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。
set方法返回的是当前的Map对象,因此可以采用链式写法。
Map.prototype.get(key):get方法读取key对应的键值,如果找不到key,返回undefined。Map.prototype.has(key):has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。Map.prototype.delete(key):delete方法删除某个键,返回true。如果删除失败,返回false。Map.prototype.clear():clear方法清除所有成员,没有返回值。
- 遍历方法:和Set大致相同,可以参考上面。
- Set结构的实例默认可以遍历,并且
entries方法就是它默认调用的遍历方法。
Map与其他数据类型的转换
- Map 转 Array
[...map] // 解构
- Array 转 Map
new Map(arr) // 数组需要每个成员都有键值对
- Map 转 对象 因为对象的key只能是字符串,因此如果Map中所有的key都是字符串格式,那么转换就不会有差异。
let temp = {}
for(let [key, value] of map) {
temp[key] = value
}
如果map的key中有非字符串,那么就会被转换成字符串,当作对象的键名。
-
对象 转 Map 调用
Object.entries()可以获取到对象的key/value组成的数组。 -
Map 转 JSON
如果map的键名都是字符串类型,则可以先将map转为对象,再使用JSON.stringify()转为JSON格式。
如果map的键名包含引用类型,那就可以将map转为数组,再使用JSON.stringify()转为JSON格式。
- JSON 转 Map
使用
JSON.parse(),得到对象或者数组,再调用相应方法转为map。
总结
多看书,多动手!