100道前端面试题(一):JS垃圾回收场景拓展之WeakMap与WeakSet

176 阅读4分钟

前言

1-1.webp

此专栏记录了在准备找工作的时候,收集的一些较好的前端面试题,希望借助这个机会,来和大家一起分享一下,还望大家多多指出我的不足。

在昨天的文章中我们提到过,在vue实际开发存在的一些内存泄漏的场景,想了解的同学可以移步到juejin.cn/post/723302…

今天我们来以此为基础,拓展一下WeakMap与WeakSet


1.什么是WeakMap(弱引用)?

  • ES6新增语法,一种集合类型,与Map相比,它的键只能是object或者继承至object的类型(直接受对象作为键),并且它不具备可迭代性,自然的,没有如Map()那样,可以通过entries()方法或者Symbol.iterator属性进行迭代。
  • 同Map()一样,WeakMap也拥有set()、has()、get()、delete()这些分别用于添加键值对、查询、获取值、删除键值对的方法
const wm = new WeakMap()

     const key1 = {id : 1}
     const key2 = {id : 2};

     alert(wm.has(key1))  // false
     alert(wm.get(key1))  // undefined

     wm.set(key1, '好恶发')
       .set(key2, '前端菜鸟')

     alert(wm.has(key1))  // true
     alert(wm.get(key1))  // 好恶发

     wm.delete(key2)

     alert(wm.has(key1))  // true
     alert(wm.has(key2))  // false

  • 在映射中用作键和值的对象以及其它“集合”的类型,在自己内容或者属性被修改时,仍然保持不变
const m = new WeakMap()

    const objKey = {},
          objVal = {},
          arrKey = [],
          arrVal = [];

    m.set(objKey, objVal);      
    m.set(arrKey, arrVal);

     objKey.foo = "foo";
     objVal.bar = "bar";
     arrKey.push("foo");
     arrVal.push("bar");
    // 修改键值的属性,并不会影响到它自身内部的映射关系
    console.log(m.get(objKey));   // {bar: "bar"}
    console.log(m.get(arrKey));   // ["bar"]
    console.log(typeof objKey)    // object

关于WeakMap的详细情况,可以看看这篇大佬写的文章:juejin.cn/post/684490…

1.1WeakMap与垃圾回收

WeakMap中的Weak表示在弱引用中,它们所设置的键不属于正式的引用,无法阻止垃圾回机制,下面我们来比较一下强弱引用的在垃圾回收机制中的不同

  • 强引用
let map = new Map(); // 弱引用
let key = new Object('a')
      function fn1() {
            map.set(key, 1) 
            console.log(map.get(key));
      }
      fn1()

此段代码中,即使fn1()函数执行完毕,局部作用域被销毁,局部变量obj仍然无法被销毁,这是因为map对 key所引用的对象的强引用依旧存在,只有手动释放内存之后,局部变量obj才在下一次的回收中才会被销毁

手动释放内存

map.delete(key)首先删除键值对,解除map对Obj的强引用,key = null解除key对Obj的强引用,此时变量key才会被垃圾回收机制清除,以上两步,少一步都会导致变量key无法被回收。

let map = new Map(); // 弱引用
let key = new Object('a')
      function fn1() {
            map.set(key, 1) 
            console.log(map.get(key));
            map.delete(key)
            key = null
      }
      fn1()
  • 弱引用

释放内存

let wMap = new WeakMap(); // 弱引用
let key = new Object('a')
      function fn1() {
         wMap.set(key, 1) 
            console.log(wMap.get(key));
            key = null
      }
      fn1()
  • 我们可以观察到,在弱引用的情况下,不需要使用wMap.delete(key)解除wMap对key的弱引用,直接解除掉key对Object的强引用,就可以使得key被回收

2.什么是WeakSet(弱集合)?

  • ES6新增语法,一种集合类型,与WeakMap相似,它的值只能是object或者继承至object的类型,尝试使用非对象类型设置值会抛出TypeError,并且它不具备可迭代性,不能如Set()那样,可以通过entries()方法或者Symbol.iterator属性进行迭代。
  • 同Set()一样,WeakMap也拥有add()、has()、delete()这些分别用于增加值、查询、删除元素的方法
const ws = new WeakSet();
     const val1 = {id : 1}
     const val2 = {id : 2}
     
     alert(ws.has(val1)); // false

     ws.add(val1)
       .add(val2)

    alert(ws.has(val1)); // true
    alert(ws.has(val2)); // true

    ws.delete(val1)

    alert(ws.has(val1)); // false
    alert(ws.has(val2)); // true
    

  • 可以使用数组初始化弱集合
const val1 = {id : 1}
     const val2 = {id : 2}
     const val3 = {id : 3}
    //  使用数组初始化弱集合
    const wsl = new WeakSet([val1, val2, val3]) 
   
    alert(wsl.has(val1))
    alert(wsl.has(val2))
    alert(wsl.has(val3))
    //  初始化是全有或者全无的操作
    // 只要有一个值无效就会抛出错误,导致整个初始化失败

2.1WeakSet与垃圾回收

WeakSet中'Weak'表示弱集合不属于正式的引用,不会阻止垃圾回收

const ws = new WeakSet()

const container = {
  val: {}
}
 ws.add(container.val)
 
 function() removeRefetence() {
     container.val = null
 }
  • 原理和WeakMap类似,container维持着对Obj的引用,销毁他们之间的引用后,就会摧毁对象值的最后一个引用,垃圾回收即将清理这个值

3.小结

  • 在实际开发中使用WeakMap初始化对象,可以使得对象具有自己属性和方法的同时,又降低内存泄漏的风险
  • 使用WeakMap和使用WeakSet时,无法使用通过entries()方法或者Symbol.iterator属性进行迭代