《用得上的前端知识》系列 - 你我都很忙,能用100字说清楚,绝不写万字长文
基本概念
强引用:除 WeakSet、WeakMap 以外的对象引用都是强引用;被强引用的对象不会被回收; 弱引用:WeakSet、WeakMap 中对对象的引用是弱引用,也就是说,即使WeakSet/WeakMap"引用"了某个对象,但垃圾回收不把这种引用计为“引用”,只要其他地方没有强引用这个对象,该对象就不可达,任何时刻可能被回收。
WeakSet
属性和方法
WeakSet 的用法和特性与 Set 类似,但不能遍历,也没有 size 属性,只有以下方法:
- Set.prototype.add(value):添加某个值,返回 Set 结构本身;
- Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功;
- Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员;
WeakSet 和 Set 的区别
- Set 对象可遍历、可清除、有 szie 属性;WeakSet 对象不可遍历、不可清除,也没有 size 属性;
- Set 对象可储存任意类型的数据,且对对象的引用为强引用;WeakSet 对象只能储存除 null 以外的引用类型数据,且对对象的引用为弱引用;
WeakMap
定义和说明
WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是除 null 以外的对象,而值可以是任意的。
属性和方法
- Map.prototype.set(key, value),set方法设置键名key对应的键值为value,然后返回整个 Map 结构。因此Map结构可以使用链式写法;
- Map.prototype.get(key),get方法读取key对应的键值,如果找不到key,返回undefined;
- Map.prototype.has(key),has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中;
- Map.prototype.delete(key),delete方法删除某个键,返回true。如果删除失败,返回false。
WeakMap 和 Map 的区别
- Map 对象的key 可以是任何类型的数据,且Map 对 key 引用的对象是强引用;WeakMap 对象的 key 只能是除 null 以外的对象,且 WeakMap 对 key 引用的对象为弱引用(垃圾回收不考虑这种引用)。
应用场景(若引用的变量随时会被删除,有何用?)
场景1:
let myElement = document.getElementById('logo');
let myWeakmap = new WeakMap();
myWeakmap.set(myElement, {timesClicked: 0});
myElement.addEventListener('click', function() {
let logoData = myWeakmap.get(myElement);
logoData.timesClicked++;
}, false);
上面代码中,myElement 是一个 DOM 节点,每当发生click事件,就更新一下状态。我们将这个状态作为键值放在 WeakMap 里,对应的键名就是myElement。一旦这个 DOM 节点删除,该状态就会自动消失,不存在内存泄漏风险。
场景2:
// html
<div id="WeakMap"></div>
// js
const wm = new WeakMap();
const weakMap = document.getElementById('WeakMap');
wm.set(weakMap, 'some information');
wm.get(weakMap) //"some information"
好处在于,当 DOM 元素移除时,对应 WeakMap 记录也会自动移除。
场景3:
// html
<button id="button1">按钮1</button>
<button id="button2">按钮2</button>
// js
const button1 = document.getElementById('button1');
const button2 = document.getElementById('button2');
const handler1 = () => { console.log("button1 被点击") };
const handler2 = () => { console.log("button2 被点击") };
// 代码1
button1.addEventListener('click', handler1, false);
button2.addEventListener('click', handler2, false);
// 代码2
const listener = new WeakMap();
listener.set(button1, handler1);
listener.set(button2, handler2);
button1.addEventListener('click', listener.get(button1), false);
button2.addEventListener('click', listener.get(button2), false);
好处是:由于监听函数是放在 WeakMap 里面,则一旦 DOM 对象 button1 / button2 消失,与它绑定的监听函数 handler1 和 handler2 也会自动消失。
验证垃圾回收
通过定时器实时查看 WeakSet 的值:
var ws = new WeakSet(); ws.add([1,2,3,4]); ws.add(null); setTimeout(function(){ console.log('res:', ws); // 还没使用,值可能就被回收了 }, 16000); setInterval(()=>{ console.log(ws); }, 1000);
注意:控制台不会进行垃圾回收,所以上述代码在控制台中运行时不会出现预期的结果。