es6: 新增的数据结构Set和WeakSet的用法和区别


Set:Set 是一个集合,用于存储唯一的值,不允许重复。它的特点包括:

  • 存储的值可以是任意类型的,可以是基本类型或对象引用。

  • 内部的值是按照插入顺序进行存储的。

  • 检查一个值是否存在于 Set 中的操作具有很高的性能。

  • 可以使用迭代器来遍历 Set 中的值。

  • Set 拥有一系列的方法,如添加值、删除值、判断值是否存在等。

下面是 Set 常见的操作方法

  1.  add(value) : 向 Set 中添加一个新的值。

  2.  delete(value) : 从 Set 中删除一个值。、

  3.  has(value) : 判断 Set 中是否包含某个值。

  4.  size : 返回 Set 中值的数量。

  5.  clear() : 清空 Set 中的所有值。

const set = new Set();

set.add(1);
set.add('Hello');
set.add({ name: 'John' });

console.log(set.size); // 输出 3

set.delete('Hello');

console.log(set.has(1)); // 输出 true

set.forEach(value => console.log(value));
```


set应用场景有很多,经常用到的就是 数组去重

const arr = [1, 2, 2, 3, 3, 4, 5]
//展开解构,将 Set 转回数组
const res = [...new Set(arr)] 
console.log(res)  //[ 1, 2, 3, 4, 5 ]


其次还有 求数据的交集,并集 ,差集会经常用到

const set1 = new Set([1, 2, 3]);  
const set2 = new Set([2, 3, 4]);  
  
// 求交集  
const res = new Set([...set1].filter(item => set2.has(item))); //返回共有的元素
console.log([...res]); // [2, 3]  
  
// 求并集  
const res2 = new Set([...set1, ...set2]);  
console.log([...res2]); // [1, 2, 3, 4]  
  
// 求差集  
const res3 = new Set([...set1].filter(item => !set2.has(item)));  
console.log([...res3]); // [1]


WeakSet:WeakSet 与 Set 类似,也是用于存储一组唯一的值,但有一些不同之处:

  • WeakSet 中只能存储对象引用,不能存储基本类型的值。

  • WeakSet 中存储的对象是弱引用,不会阻止对象被垃圾回收器回收。

  • WeakSet 没有迭代器方法,也没有办法获取 WeakSet 的大小。


WeakSet 的主要用途是存储临时对象的引用,在某些场景下非常有用,比如需要在对象被销毁时执行一些特定的操作或者需要跟踪对象的存活状态,这些对象在不再需要时会被自动回收。可以帮助我们避免内存泄漏的问题,并且使垃圾回收更加高效。

// 1. 使用 WeakSet 跟踪对象的存活状态
const weakset = new WeakSet()

const obj1 = { name: '小米' }
const obj2 = { name: '小露' }

weakset.add(obj1)  //添加对象1
weakset.add(obj2)  //添加对象2

console.log(weakset.has(obj1)); // true
weakset.delete(obj1) // 执行一些清理操作
console.log(weakset.has(obj1)); // false

//使用Set 与 WeakSet进行结果对比
const set = new Set()
set.add(obj1)  //添加对象1
set.add(obj2)  //添加对象2
console.log(set.has(obj1)); // true
weakset.delete(obj1) // 执行一些清理操作
console.log(set.has(obj1)); // true
// 2. 需要在对象被销毁时执行一些特定的操作,避免内存泄漏
<body>
    <div id="wrap">
        <button id="btn">确认</button>
    </div>
</body>
<script>
    const wrap = document.getElementById("wrap");
    const btn = document.getElementById("btn");

    //给btn打上标签
    const disableElements = new weakSet()  //若使用new Set(),会造成内存泄漏
    disableElements.add(btn)

    btn.addEventListener("click",() => { //给按钮添加点击事件
        wrap.removeChild(btn) //按钮点击完成后将其销毁
        console.log(disableElements); //理论上认为为{}
    })
</script>

示例一中,使用 WeakSet 跟踪对象的存活状态。通过调用 WeakSet 的 add 方法将对象添加到 weakset 实例中,然后可以使用 has 方法来检查对象是否仍然存活。在调用 WeakSet 的delete方法时,我们从 weakset 实例中删除对象,会发现如果一个对象被清除了,再调用has方法去查找该对象结果会返回false;而当使用 Set存储对象时,对象被清除了再查找结果还是true。所以我们可以使用 WeakSet 来跟踪对象的存活状态。

示例二中,使用WeakSet来避免内存泄漏。我们在 body 里面创建了一个 button 按钮元素,并且向 button 按钮绑定了一个点击监听事件,然后用了一个弱引用容器 disableElements 存放按钮标签,在我们点击 button 按钮时,会触发监听事件,将 button 按钮从页面中移除。由于 disableElements  中存储的是弱引用,所以当 button 按钮被移除页面后,垃圾回收器会自动回收内存,避免了内存泄漏的问题。而如果我们的 disableElements 是用Set创建的容器,那么当执行按钮点击事件后,按钮被移除页面后,则会因为 button 对象被 disableElements所引用依旧保留在内存中,不会被垃圾回收器自动回收内存,造成内存泄漏。所以,在这种应用场景下使用WeakSet容器,就解决了内存泄漏的问题。

总结:
Set 适用于需要存储一组唯一值的场景,并且需要对值进行遍历和操作。WeakSet 适用于存储对象引用的场景,并且不需要遍历和操作存储的值。在使用 WeakSet 时,需要注意对象的生命周期,避免对象被垃圾回收后仍然存在于 WeakSet 中。

声明:BenBonBen博客|版权所有,违者必究|如未注明,均为原创

转载:转载请注明原文链接 - es6: 新增的数据结构Set和WeakSet的用法和区别


过去太迟,未来太远,当下最好