ES6中的WeakSet与WeakMap

? 前言:之前学习ES6中Set和Map只管用就完事了,完全忽略了WeakSet和WeakMap,结果面试官一问就懵逼了,赶紧踏踏实实学习学习

WeakSet

为啥需要WeakSet

要弄懂这个问题,首先需要了解JS的垃圾回收机制,JS现代教程写得十分清楚明白,先补习一波,就可以知道如果一个对象没有被引用,则会被视为垃圾并回收该对象的内存空间,当我们将对象存储在Set的实例中与存储在下图的user变量一样,只要Set实列中的引用存在,垃圾回收机制就不能将该对象视为垃圾并清除。

f99100a5c02a434e8042fcbad552d1d.png

  let set =new Set(),key={};
  set.add(key)
  console.log(set.size) //1
  //移除原始引用
  key=null
  console.log(set.size) //1
  key=[...set][0] //取回原始引用
复制代码

上例中就是当我们设置key的值为null,清除了原始引用,但Set实例中任存在着一个对该对象的强引用,导致最终我们还能访问到该对象,这样的话会导致内存无法释放,进而可能会引发内存泄漏。所以,为了解决这个问题,WeakSet类型就出现了,看名字就可以知道,弱引用Set集合,WeakSet 只存储对象的弱引用,垃圾回收机制不考虑 WeakSet 对该对象的引用,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存。

特性

  • WeakSet结构同样不会存储重复的值,它的成员只能是对象类型。
  • 因为WeakSet 是弱引用,垃圾回收机制可能随时清除其中的对象,所以不可以进行forEach( )遍历、for-of循环等迭代操作
  • 也是因为弱引用,WeakSet 结构没有keys( ),values( ),entries( )等方法和size属性

声明定义

WeakSet 是一个构造函数,可以接受一个数组或类似数组的对象作为参数 ,可以使用new命令,创建 WeakSet 数据结构。

const a = [[1, 2], [3, 4]];
const ws = new WeakSet(a); // WeakSet {[1, 2], [3, 4]}
复制代码

基本操作

由于弱引用带来的限制,WeakSet只支持三种基本操作。

  • WeakSet.prototype.add(value):向 WeakSet 实例添加一个新成员。
  • WeakSet.prototype.delete(value):清除 WeakSet 实例的指定成员。
  • WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在 WeakSet 实例之中。
const ws = new WeakSet();
const obj = {};
const foo = {};

ws.add(window);
ws.add(obj);

ws.has(window); // true
ws.has(foo);    // false

ws.delete(window);
ws.has(window);    // false
复制代码

具体使用场景

根据上文我们可以得知,WeakSet 适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在 WeakSet 里面的引用就会自动消失。具体一个场景就是存储DOM对象,当我们存储的DOM对象元素被另外一段脚本移除,我们也不想保留这些元素的引用而造成内存泄漏,就可以使用WeakSet来存储。

?存储DOM元素
const ws = new WeakSet();
document.querySelectorAll("button").forEach(item => Ws.add(item));
复制代码

WeakMap

为啥需要WeakMap

相对的,WeakMap也是弱引用的Map集合,所以同理,希望避免Map集合中保存对象的强引用而导致的内存泄漏问题,使用WeakMap数据结构来保存对象的弱引用,与WeakSet不同的是WeakMap中存储的是键值对,WeaMap对键名是弱引用的,键值是正常引用,如果键在其他地方不被引用时,垃圾回收机制就会自动回收这个对象所占用的内存空间,同时移除WeakMap中的键值对,但键名对应的值如果是一个对象,则保存的是对象的强引用,不会触发垃圾回收机制被回收。

特性

  • WeakMap中存储的是许多键值对的无序列表,列表的键名必须是非null的对象,对应的值可以是任意类型
  • WeaMap对键名是弱引用的,键值是正常引用
  • 因为WeakMap 是弱引用,垃圾回收机制可能随时清除其中的对象,所以不可以进行forEach( )遍历等操作
  • 因为弱引用,WeaMap 结构没有keys( ),values( ),entries( )等方法和 size 属性

声明定义

  1. WeakMap 可以使用 set 方法添加成员
  2. WeakMap 也可以接受一个数组,作为构造函数的参数
// WeakMap 可以使用 set 方法添加成员
const wm1 = new WeakMap();
const key = {foo: 1};
wm1.set(key, 2);
wm1.get(key) // 2

// WeakMap 也可以接受一个数组,
// 作为构造函数的参数
const k1 = [1, 2, 3];
const k2 = [4, 5, 6];
const wm2 = new WeakMap([[k1, 'foo'], [k2, 'bar']]);
wm2.get(k2) // "bar"
复制代码

基本操作

因为垃圾回收机制的运行无法预测,所以 没有办法列出所有键名,某个键名是否存在完全不可预测,,为了防止出现不确定性,就统一规定不能取到键名,二是无法清空,即不支持clear方法。因此,WeakMap只有四个方法可用:get()set()has()delete()

  • WeakMap.prototype.set(key,value):向 WeakMap 实例添加一个新成员,当前的WeakMap对象。
  • WeakMap.prototype.delete(key):清除 WeakMap 实例的指定成员,返回true。如果删除失败,返回false。。
  • WeakMap.prototype.has(key):返回一个布尔值,表示某个值是否在 WeakMap 实例之中。
  • WeakMap.prototype.get(key):get方法读取key对应的键值,如果找不到key,返回undefined
const wm = new WeakMap();
let key = {};
let obj = {foo: 1};

wm.set(key, obj);

wm.get(key); //{foo: 1}

wm.has(key); //true

wm.delete(key);//true
复制代码

具体使用场景

WeakMap的专用场合就是,它的键所对应的对象,可能会在将来消失,如果我们要往对象上添加数据,又不想干扰垃圾回收机制,就可以使用 WeakMap。

?存储DOM元素

将一个 DOM 节点作为键名存入该实例,并将一些附加信息作为键值,一起存放在 WeakMap 里面。这时,WeakMap 里面对element的引用就是弱引用,一旦将这个 DOM 节点删除,该element对象就会自动被垃圾回收机制清除,不存在内存泄漏风险。

<body>
  <div>bar</div>
  <div>foo</div>
</body>
<script>
  const wm = new WeakMap();
  document
    .querySelectorAll("div")
    .forEach(item => wm.set(item, item.innerHTML));
  console.log(wm); //WeakMap {div => "bar", div => "foo"}
</script>
复制代码
?存储私有变量

ES5中我们经常利用立即执行函数的方式来设置私有变量,但问题是私有变量不会随着实例对象的销毁被回收,WeakMap正好可以解决这个问题。

let Person=(function(){
    let privateData = new WeakMap();
    function Person(name){
        privateData.set(this,{name})
    }
    Person.prototype.getName = function(){
        return privateData.get(this).name
    }
    return Person
    
}())
复制代码

当调用Person构造函数时,实例就会被添加到WeakMap集合中,键是this, 是实例的弱引用,值是私有属性name的对象, 如果删除实例,私有属性也就随之消失,不会造成内存泄漏。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享