读懂ES6的Set对象并跟Array优劣势对比

795 阅读4分钟

引言

在还没认识ES6的Set对象之前,我一直使用JS的原生数组Array的方法,虽然在开发中一样能实现想要的效果,但在使用Set之后,不得不说一句真香。

作为开发者首先要明白,为什么ES会设计一种新的数据结构去替代Array的部分使用场景,本文将一点点讲解Set跟Array的区别跟各自的优势和应用场景。

Set跟Array最明显的区别就是它的成员的值是唯一的,它在prototype上的方法相对Array比较简洁和直观。同时可以看出来Set对象是继承自Object对象。

image.png

基本用法

只能用new去定义一个Set对象,可以接受一个数组作为参数初始化

const a = new Set() 
[...a] // []

const b = new Set([1,2,3,4,5,5,5]) 
[...b] // [ 1, 2, 3, 4, 5 ]

Set对象可用于数组去重

由于成员的值是唯一的,因此上述b的成员不会出现同样的元素5 利用这一特性,可用下面的方法给数组去重

[...new Set([arr])

同时也可给字符串清除重复字符

[...new Set('aaabnbv')].join('') // abnv

操作方法

常用的方法有四种add,delete,has,clear

add

添加元素,返回 Set 结构本身

a.add(1).add(2).add(1);
a.size // 2

has

判断是否为成员,返回一个Boolean

[...a] // [1,2]
a.has(1) // true
a.has(2) // true
a.has(3) // false

跟普通Array对象的判断成员方法对比,跟ES6新出的includes异曲同工,但比传统的indexOf更简洁

const arr = [...a]
// 使用includes
arr.includes(1) // true
arr.includes(2) // true
arr.includes(3) // false
// 使用indexOf
arr.indexOf(1)==-1 // false
arr.indexOf(2)==-1 // false
arr.indexOf(3)==-1 // true

跟普通Object对象对比

a[key]

delele

删除某个值,返回一个布尔值,表示删除是否成功

[...a] // [1,2]
a.delete(1) // true
a.delete(4) // false
a // 2

clear

清除所有成员,没有返回值

a.clear() // undefined
a // Set(0) {}

遍历方法

  • Set.prototype.keys():返回键名的遍历器
  • Set.prototype.values():返回键值的遍历器
  • Set.prototype.entries():返回键值对的遍历器
  • Set.prototype.forEach():使用回调函数遍历每个成员

keys()values()entries()

由于Set没有键名,所以keys,values的效果一样,entries则返回一个数组,两个成员都是Set的值。

let set = new Set([1,2,3]);

for (let item of set.keys()) {
  console.log(item);
}
// 1
// 2
// 3

for (let item of set.entries()) {
  console.log(item);
}
// [1, 1]
// [2, 2]
// [3, 3]

forEach

用于遍历Set对象中的值,同上这里的value跟key是一样的

set.forEach( (value,key) => {
    console.log(value)
})

同时,mapfilter也可以间接用于Set

let set = new Set([1, 2, 3]);
set = new Set([...set].map(x => x * 2));
// 返回Set结构:{2, 4, 6}

let set = new Set([1, 2, 3, 4, 5]);
set = new Set([...set].filter(x => (x % 2) == 0));
// 返回Set结构:{2, 4}

WeakSet

还有一个Set对象的衍生品WeakSet,用于处理其他情况。 可以看出来它具备部分的Set语法,但有两个区别

image.png

成员只能对象

const weak = new WeakSet()
weak.add(1)

image.png

弱引用:垃圾回收不考虑

了解V8垃圾回收机制的应该知道,当一个对象还能被访问到,回收机制将不会释放该内存,当使用后没有释放该内存,容易引发内存泄漏。 但在WeakSet里面引用的对象,不会计入回收机制,当该对象不能被访问后,系统将会清除该对象,而不考虑它被WeakSet引用了。在此之后,它在WeakSet里面的引用就消失了。 由于上面这个特点,WeakSet 的成员是不适合引用的,因为它会随时消失。

WeakSet 无法遍历

WeakSet 不能遍历,是因为成员都是弱引用,随时可能消失,遍历机制无法保证成员的存在,很可能刚刚遍历结束,成员就取不到了。WeakSet 的一个用处,是储存 DOM 节点,而不用担心这些节点从文档移除时,会引发内存泄漏

使用Set的优势

从根本上来说,Array是根据索引排序,而Set是根据插入顺序迭代。主要优势体现在以下几个方面

  1. 查看元素:has比includes,indexOf的查看速度要快
  2. 删除元素:可根据值去删除元素,而Array只能通过splice+索引的方式,对索引的依赖更大
  3. 无重复元素:上述已说明

但创建元素的方法,Array会比Set速度更快。

有一篇文章对比不同数据量下两种对象的速度,可以参考一下 juejin.cn/post/684490…