Set和Map
ES6之前,JavaScript只有一种集合类型,那就是数组
。和其他语言相比,这实在是显得寒酸了些。虽然对象
常被作为键值对
的集合形式来使用,但对象会受限于其自身的设计和特性,为了统一和补充集合,ES6标准诞生了两种新的集合:Set和Map。
Set
Set集合是一种列表,其最特殊的地方在于: 其中的元素是没有重复
的。来看一个小例子:
javascriptlet set = new Set() set.add(5) set.add(5) set.add('5') set.has(5) // true set.delete(5) set.has(5) // false console.log(set.size) // 1 set.clear() console.log(set.size) // 0
上面代码中,先是创建了一个空的set集合,再通过add()
方法添加了一些元素,随后,又删除了元素5,最终,通过clear()
方法彻底清空了set集合。
Set和数组
对于Set而言,可以通过forEach()
来遍历其中的数据,但是它无法像数组一样,通过索引来访问其中的元素,对于使用Set的场景而言,我们一般不关心其中数据的顺序,我们更关心的是其自身去重的功能。在某些请下,我们需要对Set和数组之间进行转换,比如,对数组去重:
javascriptlet set = new Set([1, 2, 3, 4, 5, 5, 5]) let arr = [...set]
从上面代码可以看出,数组要转换为Set,只需要通过Set构造函数即可,就会得到一个没有重复值的Set集合,最后,当然还需要再从Set集合转换为数组类型,通过展开运算符可以将Set中的值逐一展开,然后放入数组中即可。
WeakSet
WeakSet是什么东西呢?既然有了Set,为啥还需要搞出这个?我们先来看一个场景:
javascriptlet set = new Set() let dom = { id: 'app', type: 'div' } set.add(dom) dom = null // 希望回收这个DOM节点 // 依旧可以获得希望被垃圾回收的dom对象的引用 let reKey = [...set][0] console.log(reKey.type) // "div"
上面代码中,有一个dom节点对象被添加到了一个Set集合中,当不再需要的时候,我们希望垃圾回收器能够将其回收掉,所以将dom赋值为null,但是很遗憾,最终这个dom值并没有被回收掉,这导致了内存泄露
。所以,WeakSet就是为了解决这个问题的。
javascriptlet set2 = new WeakSet() let dom = { id: 'app', type: 'div' } set2.add(dom) dom = null
当上面代码中dom被赋值为null后,set2中的dom引用也会被自动移除,再次访问set中的那个dom元素的属性就会报错(因为其已经被移除了)。
虽然WeakSet和Set有很多相同之处,但是,他们也有一些差别:
- 在WeakSet的实例中,add()方法
只能接收对象
,其他类型(字符串,数字,boolean等)都会报错 - WeakSet不可迭代,无法使用for-of循环
- WeakSet不支持forEach()方法
- WeakSet不支持size属性
WeakSet看上去有点“弱”,这是对的,因为一般情况,都会选择使用Set,而这个WeakSet仅用在特殊
的场景,它能自动移除希望被垃圾回收器回收的对象,当你需要跟踪对象,并防止内存泄露的场景下,才选择WeakSet。
Map
Map是一种存储键值对
的列表,它和对象很像,因为对象也是键值对的形式,但是它们之间还是有很多差别的,对象中的属性总会被强制转换为字符串
类型,比如对象中:
javascriptlet obj = {} key1 = 5 key2 = '5' obj[key1] = 100 obj[key2] = 200 console.log(obj) // {5: 200}
而在Map中:
javascriptlet map = new Map() map.set(5, 'number') map.set('5', 'string') console.log(map) console.log(map.get(5)) // 'number'
键5(数字)和键‘5’(字符串)是两个不同的键,不会像对象的属性一样,会被强制转换。
Map中支持的方法和Set类似,有如下几种常见方法:
- set(key, value):设置指定的键值对
- get(key):获得指定键的值
- has(key):检测指定的键名是否存在
- delete(key):删除指定的键名以及对应的值
- clear():移除Map中的所有键值对
注意:Set集合用add()
来添加值,而Map集合用set()
来设置键值对。
可以向Map构造函数传入数组来初始化一个Map集合:
javascriptlet arr = [ ['name', 'foo'], ['age', 30] ] let map2 = new Map(arr) console.log(map2.get('age')) // 30
注意,其需要传入一个二维数组,且内部的子数组中包含一个键和一个值。
参考
- 深入理解ES6 / Nicholas C. Zakas