likes
comments
collection
share

es6中Set和WeakSet的用法和区别

作者站长头像
站长
· 阅读数 48

ES6中引入了SetWeakSet两种新的数据结构,它们主要用于处理不重复值的集合。在面试中也极易被问到WeakSet()的弱引用是怎么理解的,书上的概念晦涩难懂,必须将实际结合起来理解。下面我将会层层递进说明。

Set

  1. 定义与用法

    • Set是一个集合类型,用于存储唯一值,无论是原始类型值(如字符串、数字)还是对象引用。
    • 创建一个Set实例可以通过new Set()来完成,可以接受一个可迭代对象(如数组、字符串)作为参数,将其中的元素添加到Set中。
    let set = new Set([1, 2, 3, 4, 4, 3]); // set现在包含1, 2, 3, 4
    
  2. Set 的常用方法

Set 提供了丰富的API来处理集合数据,包括:

  • add(value) : 向集合中添加一个新的元素。如果元素已存在,则不会重复添加。

    ```
    let s = new Set();
    s.add(1);
    ```
    
    • delete(value) : 如果集合中存在指定的元素,则删除它并返回true,否则返回false

      s.delete(1);
      
    • has(value) : 检查集合中是否包含指定的元素,返回truefalse

      s.has(1);
      
    • clear() : 清空集合中的所有元素。

      s.clear();
      
    • size: 只读属性,返回集合中元素的数量。

    • forEach(callback[, thisArg]) : 对集合中的每个元素执行给定的函数。

  1. 特性

    • 成员值唯一,自动去除重复项。
    let numbers = [1, 2, 3, 4, 4, 3, 5];
    let uniqueNumbers = new Set(numbers);
    console.log([...uniqueNumbers]); // 输出: [1, 2, 3, 4, 5]
    
    • 可以进行集合操作,如并集、交集等。
    const set1 = new Set([1, 2]); 
    const set2 = new Set([2, 3]);
    // 求并集 
    const res2 = new Set([...set1, ...set2]); 
    console.log([...res2]); // [1, 2, 3]
    
     // 求交集 
     const res = new Set([...set1].filter(item => set2.has(item))); //返回共有的元素 
     console.log([...res]); // [2]
    
    • 支持迭代(例如,可以使用for...of循环遍历)。
    let letters = new Set(['a', 'b', 'c']); 
    for(let letter of letters) { 
        console.log(letter); 
    } // 输出: // a // b // c
    

WeakSet

  1. 定义与用法

    • WeakSet同样用于存储唯一对象,但与Set不同的是,它只接受对象作为成员,并且这些对象是以弱引用的方式存储的。
    • 创建一个WeakSet实例也是通过new WeakSet(),但它的构造函数不接受数组等可迭代对象作为参数,而是接受一个或多个对象作为单独的参数或使用add方法添加。
    let obj1 = {};
    let weakSet = new WeakSet([obj1]);
    
  2. WeakSet 的常用方法

由于WeakSet的设计主要用于持有对象的弱引用,它的API相对精简:

  • add(value) : 向集合中添加一个对象。如果对象已经存在于集合中,则此操作无效果。
  • delete(value) : 如果集合中存在指定的对象,则将其移除并返回true,否则返回false
  • has(value) : 检查集合中是否包含指定的对象,返回truefalse

注意,WeakSet没有clear方法,也不支持遍历操作(如forEach、不能用在for...of循环中),因为它是不可迭代的,且没有size属性来获取元素数量,这都是为了保持其内部对象的弱引用特性,避免影响垃圾回收。

  1. 特性

    • 成员必须是对象,
    let obj1 = { id: 1 }; 
    let obj2 = { id: 2 }; 
    let weakSet = new WeakSet([obj1, obj2]); 
    
    // 尝试添加非对象值会报错(注:严格模式下) 
    weakSet.add(3); // TypeError: Invalid value used in weak set
    
    • 是弱引用,这意味着如果对象没有其他引用指向它,垃圾回收机制会自动回收该对象,即使它在WeakSet中。

    let obj = {};
    let ws = new WeakSet([obj]);
    // 执行中垃圾回收是随机发生的,我们无法预判,但当一个值被赋值为null时,则表示垃圾回收在此刻发生
    obj = null;
    // 在这一时刻,若没有其他对obj的引用,垃圾回收机制会回收obj对象,
    // 而不会因为ws中仍有对该对象的引用而不去回收obj对象。
    
    • 不可迭代,没有大小属性,也不支持forEach等方法。
    // weakSet.size; // undefined, WeakSet没有size属性 // for(let item of ws) { console.log(item); } // 这会报错,因为WeakSet不可迭代
    
    • 由于其弱引用的特性,WeakSet主要用于跟踪对象,而不用担心阻止对象被垃圾回收。
    • 总结:一个对象obj存在了其他的结果中,当后续只存在weakSet对它的引用,该对象的内存依然会被回收

区别总结

  • 成员类型Set可以包含任何类型的值(包括原始值和对象),而WeakSet只能包含对象。

  • 引用强度Set对成员的引用是强引用,意味着只要Set存在,它所包含的对象就不会被垃圾回收;而WeakSet中的对象引用是弱引用,对象可以被垃圾回收,即使它还在WeakSet中。

  • 迭代与大小Set是可以迭代的,可以用size属性获取成员数量;而WeakSet既不可迭代,也没有size属性。

  • 用途Set适用于需要维护唯一值集合的场景,而WeakSet通常用于需要跟踪对象但又不想阻止它们被垃圾回收的场景,如事件监听器管理等。

    如下例子:

    <div id="wrap">
        <div id="btn">确认</div>
    </div>
    
    <script>
        let wrap = document.getElementById("wrap")
        let btn = document.getElementById("btn")
    
    
        const disabledEls = new WeakSet()  // 
        disabledEls.add(btn)
    
        btn.addEventListener("click", () => {
            wrap.removeChild(btn)
        })
    </script>
    

    点击按钮后,清除按钮,这时我们不再需要按钮所产生的内存了,如果用Set()则会阻止内存回收,用WeakSet()则不会,垃圾回收后则不会产生内存泄漏。

转载自:https://juejin.cn/post/7373609752238620735
评论
请登录