Set vs WeakSet 集合类型比较

前言

在 JavaScript 中,Set 和 WeakSet 是不同的集合类型,它们在多个方面存在显著差异。本文将深入探讨它们的异同,从数据存储方式、引用特性、迭代能力到实际应用场景,帮助读者更全面地理解和运用这两种集合类型。

Set :

  1. 数据存储方式: Set是一种存储唯一值的集合,它不允许重复的元素。它可以包含任何数据类型,包括原始数据类型和对象引用。
  2. 引用: Set中的元素是强引用,这意味着当集合中的对象不再被引用时,集合仍然保留对该对象的引用。有时可能导致内存泄漏。
  3. 可迭代性: Set是可迭代的,可以使用for...of循环或forEach方法遍历集合中的元素。

WeakSet :

  1. 数据存储方式: WeakSet同样用于存储对象引用,但是它只允许存储对象引用,并且这些引用是弱引用。
  2. 引用: WeakSet中的对象引用是弱引用,这意味着如果在程序的其他地方没有对该对象的强引用,垃圾回收器可能会回收该对象,并从WeakSet中移除。
  3. 可迭代性: WeakSet不是可迭代的,没有提供直接的方法来遍历其元素,因为元素的引用是弱引用,不稳定且无法确定何时会被垃圾回收。

它们的主要区别就是以下两点:

1. 类型限制:

  • Set: Set可以包含任何类型的值,包括原始数据类型和对象引用。

    js 复制代码
    const mySet = new Set();
    mySet.add(1); // 可以存储原始数据类型
    mySet.add("Hello"); // 可以存储字符串
    const obj = { key: "value" };
    mySet.add(obj); // 可以存储对象引用
  • WeakSet: WeakSet的成员只能是对象和Symbol值,不能包含其他类型的值。

    js 复制代码
    const myWeakSet = new WeakSet();
    myWeakSet.add({}); // 只能存储对象
    const symbol = Symbol("symbol");
    myWeakSet.add(symbol); // 可以存储Symbol值
    myWeakSet.add(1); // 会报错,不能存储原始数据类型

2. 弱引用特性:

  • Set: Set中的元素是强引用,即使在程序的其他地方没有对该对象的引用,该对象仍然存在于Set中。

  • WeakSet: WeakSet中的对象是弱引用,如果在程序的其他地方没有对该对象的强引用,垃圾回收机制会自动回收该对象,同时也会从WeakSet中移除。

    js 复制代码
    const weakSet = new WeakSet();
    let obj = { key: "value" };
    
    weakSet.add(obj);
    console.log(weakSet.has(obj)); // true
    
    // 当没有其他引用指向obj时,垃圾回收机制会回收obj,并从WeakSet中移除
    obj = null;
    console.log(weakSet.has(obj)); // false

方法和遍历:

  • Set: 提供了add、delete和has等方法,以及可迭代的特性。
  • WeakSet: 提供了add、delete和has等方法,但没有可迭代的特性,也没有size属性。而且WeakSet不能遍历,因为成员都是弱引用,随时可能消失,遍历机制无法保证成员的存在,很可能刚刚遍历结束,成员就取不到了。
js 复制代码
const mySet = new Set();
mySet.add(1);
mySet.delete(1);
console.log(mySet.has(1)); // false
console.log(mySet.size); // 获取Set的大小

const myWeakSet = new WeakSet();
const obj = {};
myWeakSet.add(obj);
myWeakSet.delete(obj);
console.log(myWeakSet.has(obj)); // false
console.log(myWeakSet.size); // undefined,WeakSet没有size属性

应用场景:

  • Set: 适合需要存储任意类型值且不希望有重复值的情况。
  • WeakSet: 适合临时存放一组对象,以及存放跟对象绑定的信息。由于WeakSet中的引用不会影响垃圾回收,因此适合用于防止内存泄漏,例如存储DOM节点,确保当节点被移除时,不会阻止其被垃圾回收。

下面来个WeakSet实例:

js 复制代码
const foos = new WeakSet()
class Foo {
  constructor() {
    foos.add(this)
  }
  method () {
    if (!foos.has(this)) {
      throw new TypeError('Foo.prototype.method 只能在Foo的实例上调用!');
    }
  }
}
  • const foos = new WeakSet():创建一个WeakSet实例,用于存储Foo类的实例引用。
  • class Foo { ... }:定义了一个Foo类。
  • 在Foo类的构造函数中,foos.add(this):在foos的WeakSet中添加当前Foo类的实例。这确保了Foo的实例会被弱引用存储,不影响垃圾回收。
  • method():Foo类的实例方法,用于执行某些操作。在方法的开头使用if (!foos.has(this))检查this是否在foos中,如果不在,说明该方法不是在Foo的实例上调用的,会抛出一个TypeError,提示用户只能在Foo的实例上调用该方法。

这段代码确保了Foo的实例方法,只能在Foo的实例上调用。这里使用 WeakSet 的好处是,foos对实例的引用,不会被计入内存回收机制,所以删除实例的时候,不用考虑foos,也不会出现内存泄漏。

总结

  • 使用Set当你需要存储一组唯一的值,并且这些值在整个程序的生命周期内都是稳定的。
  • 使用WeakSet当你需要存储对象引用,并且希望这些引用是弱引用,当对象在其他地方没有被强引用时可以被垃圾回收。

最后

总的来说,WeakSet主要用于需要弱引用特性以避免内存泄漏的情况,而Set适用于一般的集合需求。希望本文能够给你带来帮助!感谢阅读!

我的Gitee: CodeSpace (gitee.com)

技术小白记录学习过程,有错误或不解的地方还请评论区留言,如果这篇文章对你有所帮助请 "点赞 收藏+关注" ,感谢支持!!

相关推荐
葡萄城技术团队6 小时前
1000 人协同压测的环境瓶颈排查:CPU 争用与 Wi\-Fi
前端
excel7 小时前
Marked 与 Markdown 有什么区别?该怎么选?
前端
Da Da 泓8 小时前
五子棋 Web 端测试用例报告
前端·网络·websocket·网络协议·测试用例·项目
zxcvb1539 小时前
远程查看本地文档:一条轻量 Web 文件服务的工程化链路
前端·远程查看文档
niucloud-admin9 小时前
NIUCLOUD PHP SAAS上门家政使用文档:城市管理
java·前端·php
程序员大阳9 小时前
副队长JavaScript教程(50)-DOM:网页开发的起点
javascript·dom·es·节点·概念·document·关系
北岛贰9 小时前
啵啵音乐迎来全新升级!
前端
hkNaruto11 小时前
【AI】基于大模型的AI游戏地图生成、控制方案:AI Native Game Harness 迁移操作手册
前端·人工智能·游戏
IT_陈寒11 小时前
Java序列化踩坑记:对象循环引用让我加班两小时
前端·人工智能·后端
每天进步一点点km11 小时前
自用-牛客网-华为机试编程模拟题1-python
华为od·华为·面试