ES6 - 新的数据结构Set

415 阅读4分钟

一种新的数据结构

定义

同数组相似,数组中可能存在重复的值,但是Set中的值是唯一的不存在重复的值,它是一个伪数组

生成set实例

初始化一个空的Set

let s = new Set()
console.log(s);                         // []

放入初始化数据

let s = new Set([1,2,3])
console.log(s);

image.png

假如有重复的值呢?

// 输出的结果会忽略重复的值
let s = new Set([1,2,3,2])
console.log(s);                      // [1,2,3]

常用方法

添加数据

支持链式操作

let s = new Set([1,2,3,2])
s.add('lee').add('ES6')
console.log(s);                      //  [1, 2, 3, 'lee', 'ES6']

删除数据

let s = new Set([1,2,3,2])
s.add('lee').add('ES6')
s.delete(2)
console.log(s);                      //  [1, 3, 'lee', 'ES6']

清空数据

let s = new Set([1,2,3,2])
s.add('lee').add('ES6')
s.delete(2)
s.clear()
console.log(s);                      //  []

判断Set中是否包含某个值

let s = new Set([1,2,3,2])
s.add('lee').add('ES6')
s.delete(2)
console.log(s.has('lee'));           //  true

获取Set的长度

let s = new Set([1,2,3,2])
s.add('lee').add('ES6')
s.delete(2)
console.log(s.size);                //  4

遍历Set

forEach

let s = new Set([1,2,3,2])
s.add('lee').add('ES6')
s.forEach(item => console.log(item))    // 1 2 3 lee ES6

for of :遍历可遍历的对象

let s = new Set([1,2,3,2])
s.add('lee').add('ES6')
for(let item of s) {
    console.log(item);                // 1 2 3 lee ES6
}   

for of keys() && for of values() && for of entries()

let s = new Set([1,2,3,2])
s.add('lee').add('ES6')
for(let item of s.keys()) {
    console.log(item);
}   
for(let item of s.values()) {
    console.log(item);
}   

image.png

====> 通过上面可以发现Set中的key和value的值是一样的

应用场景

1.数组去重

let arr = [1,2,3,4,2,3]
let s = new Set(arr)
console.log(s);            // [1,2,3,4]

2.数组合并去重

通过Set去重,通过拓展运算符合并数组(拓展运算符就是将数组中的值取出来),得到的是Set,还需要对Set进行扩展或者使用Array.from()将Set变成数组

let arr1 = [1,2,3,4]
let arr2 = [2,3,4,5,6]
let s = new Set([...arr1,...arr2])
console.log([...s]);                            // [1,2,3,4,5,6]
console.log(Array.from(s));                     // [1,2,3,4,5,6]

3. 数组的交集

let arr1 = [1,2,3,4]
let arr2 = [2,3,4,5,6]

// 数组的去重
let s1 = new Set(arr1)
let s2 = new Set(arr2)
// 数组的去重
let result = new Set(arr1.filter(item => s2.has(item)))
// 将Set转为数组
console.log([...result]);                      // [2,3,4]

4. 数组的差集

let arr1 = [1,2,3,4]
let arr2 = [2,3,4,5,6]

let s1 = new Set(arr1)
let s2 = new Set(arr2)
let r1 = new Set(arr1.filter(item => !s2.has(item)))
let r2 = new Set(arr2.filter(item => !s1.has(item)))
let r3 = [...r1,...r2]
console.log(r3);                                 // [1,5,6]

Weakset

Weakset同Set有什么区别呢?

Weakset中只能存对象

let ws = new WeakSet()
console.log(ws.add(2));                             // Ivalid value used in weak set

其他删除添加等方法同Set一样

let ws = new WeakSet()

ws.add({
    name: 'lee'
})
ws.add({
    age:5
})
console.log(ws);

删除对象的时候要注意下面这样删除对象是失败的,为什么?

// 删除失败
let ws = new WeakSet()
ws.add({
    name: 'lee'
})
ws.add({
    age:5
})
ws.delete({
    name: 'lee'
})
console.log(ws);

add添加的对象和delete删除的对象虽然一样,但是实质上在堆内存中对应2块不同的内存空间

正确的删除方法是声明变量,将堆内存中的对象对应的栈内存地址存下来,对其进行增删操作,操作对象实际上就是操作这个内存地址

let ws = new WeakSet()
let obj1 = {
    name: 'lee'
}
ws.add(obj1)
ws.add({
    age:5
})
ws.delete(obj1 )
console.log(ws);

当然它也有has方法来判断WeakSet中是否存在某个对象

let ws = new WeakSet()
let obj1 = {
    name: 'lee'
}
let obj2 = {
    age:5
}
ws.add(obj1)
ws.add(obj2)
ws.delete(obj1 )
console.log(ws.has(obj2));       // true

WeakSet 不支持遍历

let ws = new WeakSet()
let obj1 = {
    name: 'lee'
}
let obj2 = {
    age:5
}
ws.add(obj1)
ws.add(obj2)
ws.forEach( item => console.log(item))    // ws.forEach is not a function

WeakSet中对这个对象是弱引用并不会被垃圾回收机制所考虑

WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。

弱引用和垃圾回收机制

弱引用:垃圾回收机制中是不考虑WeakSet对这个对象的引用

如果其他对象不再引用当前的对象的时候,那么垃圾回收机制就会自动回收这个对象所占用的内存空间

垃圾回收机制又叫GC,现在是计数的垃圾回收机制,当前的值被引用一次那么就+1,就是+1+1+1...

只要被引用的次数不为0,那么垃圾回收机制就不会回收这个变量,也就不会释放当前的内存

如果这个值一直被引用着,那么它就不会被垃圾回收,那么如果数量越来越多,就会引发内存泄漏

应用场景

WeakSet适用于临时存放对象,或者跟对象绑定一些相关的信息(当对象销毁后,WeakSet中对这个对象存值的引用也会自动的消失)