zl程序教程

您现在的位置是:首页 >  前端

当前栏目

随笔-Set和WeakSet的使用

set 随笔 使用
2023-09-14 09:13:41 时间

Set构造函数

Set是一个构造函数或类,可以用来创建Set实例,Set构造函数支持传入一个实现了Iterable接口的对象,并自动迭代出该对象的所有非重复元素存入到Set实例中

const set1 = new Set()

const set2 = new Set([1,2,3]) // 数组对象是可迭代对象
const set3 = new Set('abc') // 字符串对象也是可迭代对象

function fn(){
    const set4 = new Set(arguments) // 函数的arguments对象
    console.log(set4)
}

const divs = document.querySelectorAll('div') // NodeList也是可迭代对象
const set5 = new Set(divs)

const lis = document.getElementsByTagName('li') // HTMLCollection也是可迭代对象
const set6 = new Set(lis)

如果new Set() 传入的不是一个可迭代对象,会报错

 某对象是否可迭代 指的是 该对象上是否 [Symbol.iterator]属性,且该属性是指向一个工厂函数,该工厂函数返回一个迭代器对象。

迭代器对象是指有next方法的对象,next方法实现迭代能力。

Set原型上的属性和方法

Set原型上的实例属性包括:

size :set实例中元素个数

这里没有用length,而是用size,我们知道数组的长度就是数组元素的个数,但是set底层是哈希表,哈希表的长度不一定是哈希表上元素的个数

Set原型上实例方法包括:

Set add(ele)  :              添加元素

boolean delete(ele) :    删除元素

undefined clear():         清空元素

boolean has(ele) :        判断元素是否存在

可以发现上述方法都是基于元素本身操作,没有基于索引操作的,这是因为Set底层数据结构是哈希表。

add方法会去重添加,它判断元素相同的标准是 ===严格相等,但是对于NaN例外,另外add方法会返回set实例,这和数组的push以及unshift不同。

delete删除元素成功返回true,删除元素失败返回false(比如删除的元素不存在)

clear清空set实例中的所有元素,没有返回值,即返回undefined

has判断元素是否存在,存在返回true,不存在返回false,我们需要对比它和数组includes方法,或者indexOf、lastIndexOf方法的区别,在功能上没有区别,但是在性能上has要比includes和indexOf或lastIndexOf快

需要注意add添加数据,和new Set添加多个数据的区别

也就是说new Set传入一个可迭代对象的话,它底层会将可迭代对象的元素都迭代出来后添加到set中,而add传入一个可迭代对象,会直接当成一个元素添加到set中 

forEach((item, index, set)=>{})

Set原型上还有一个forEach遍历方法,该方法语法和Array原型上的forEach一致,但是需要注意的是Set实例只有值,没有键。所以item和index值相同。

 

SetIterator keys():        keys方法本质是调用values方法

SetIterator values():     返回set实例上所有的元素,返回值是一个迭代器对象

SetIterator enteries():   返回set实例所有键值对(键值对表现为数组形式)

由于Set实例没有键,所以keys底层调用的是values,

 

entries返回键值对中键和值相同,这里返回都是迭代器对象,所以可以for...of迭代

需要注意的是for...of迭代出来的entries键值对是数组格式,键是数组第一个元素,值是数组第二个元素

另外Set本身也是一个可迭代对象,而Set的[Symbol.iterator]函数属性指向的是Set.prototype.values,

 所以我们一般不直接使用set实例的keys(),values(),entries()方法获得迭代器对象,而是直接使用set实例来for...of

Set转Array

由于Set是一个可迭代器对象,所以可以使用展开运算符,将Set展开为数列添加到数组中

 

还可以使用Array.from,该方法可以将一个类数组对象或可迭代对象转为数组

还可以使用for...of来迭代set元素加入数组中,和扩展运算符类似

 

Array转Set

通过new Set(arr) 就可以将arr转为set

Set的应用

1、去重 (如收集用户输入关键词)

2、并集 

3、交集

4、差集

const arr = [1,2,3,4,5]

const arr1 = [2,4,6,8,10]

function getUnion(arr,arr1) {
    return [...(new Set([...arr, ...arr1]))]
}

function getIntersection(arr,arr1){
    return arr.filter(item=>{
        return new Set(arr1).has(item)
    })
}

function getDiff(arr,arr1) {
    return arr.filter(item=>{
        return !new Set(arr1).has(item)
    })
}

WeakSet是一个构造函数或类,可以用来创建WeakSet实例。

和Set相同的是:WeakSet实例存储的元素不能重复,判同标准和Set相同

和Set不同的是:WeakSet实例只能存储对象,不能存储简单类型数据,否则报错

需要注意的是:WeakSet构造函数在new时,可以传入一个可迭代对象,但是和Set一样,WeakSet将可迭代对象的元素都迭代出来存储到自身上,而此时若迭代的元素不是对象,则也会报错。

 

WeakSet和Set另一个不同点是

WeakSet对象对存储在它身上的元素对象的引用是弱引用

Set对象对存储在他身上的元素的引用是强引用

如果一个对象被强引用关联,则不会被垃圾回收

如果一个对象仅被弱引用关联,则可能会被垃圾回收。

而这决定了WeakSet对象不需要关注存储在自身上的对象的内存回收,因为WeakSet对象对它们都是弱引用,若它们被WeakSet对象引用,则会在适当的时机自动被垃圾回收。

 由于weakset实例上保存的其他实例随时可能被垃圾回收,很不稳定,所以weakset没有size属性,也没有keys(),values(),entries(),也没有forEach方法,自身也就不能迭代,所以weakset不是可迭代对象,也就不能for...of,或者扩展运算符。

WeakSet原型上只有add,delete,has三个方法,用于添加删除判断元素

那么WeakSet的应用是什么呢?

WeakSet相比较于Set的优点是:无需考虑存储在自身上的对象的回收,不会造成内存泄漏

而Set上存储的对象,只有在Set实例被回收,或者删除掉它时,才会被回收,否则可能会造成内存泄漏。

当我们需要保存某些大型对象时,如DOM元素,则推荐使用WeakSet,而如果使用Set有内存泄漏的风险