> WeakMap结构与Map结构类似,也用于生成键值对的集合.
# 含义
**WeakMap只接受对象作为键名(null)除外**
```javascript
const map = new WeakMap();
map.set(1, 2);
// TypeError: 1 is not an object!
```
**WeakMap的键名所指向的对象不计入垃圾回收机制**
```js
const e1 = doucment.getElementById('foo');
const e2 = doucment.getElementById('bar');
const arr = [
[e1, 'foo元素'],
[e2, 'bar元素'],
];
```
上面的代码中, e1和e2是两个对象,我们通过arr数组对这两个对象添加一些文字说明,这就形成了arr对e1和e2的引用.
一旦不再需要这两个对象,我们必须手动删除这个引用,否则垃圾回收机制就不会释放e1和e2占用的内存.
```js
// 不需要e1和e2的时候
// 必须手动删除引用
arr[0] = null;
arr[1] = null;
```
上面的这样的写法显然很不方便,一旦忘了写,就会造成内存泄漏.
WeakMap就是为了解决这个问题而诞生的,它的键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内. 因此,只要所引用的对象的其他引用都被清楚,垃圾回收机制会释放该对象所占用的内存,也就是说,一旦不再需要,WeakMap里面的键名对象和所对应的键值对会自动消失,不用手动删除引用.
# 语法
WeakMap与Map在API上的区别主要有两个.
1. 没有遍历操作(即没有key(), values()和entries()方法),也没有size属性.因为没有办法列出所有键名,某个键名是否存在完全不可预测,和垃圾回收机制是否运行相关.这一刻可以取到键名,下一刻垃圾回收机制突然运行,这个键名就消失了,为了防止出现不确定性,因此统一规定不能取到键名.
2. 无法清空,即不支持clear方法,因此WeakMap只有4个方法可用:get()、set()、has()、delete()。
# 用途
WeakMap应用的典型场景就是以DOM节点作为键名的场景。
## 注册监听事件的listener对象
注册监听事件的listener对象很适合用WeakMap来实现.
```js
const listener = new WeakMap();
listener.set(element1, handler1);
listener.set(element2, handler2);
element1.addEventlistener('click', listener.get(element1), false);
element2.addEventlistener('click', listener.get(element2), false);
```
上面的代码中,监听函数放在WeakMap里面.一旦DOM对象消失,与它绑定的监听函数也会自动消失.
## 部署私有属性
WeakMap的另一个用处是部署私有属性。
```js
const _counter = new WeakMap();
const _action = new WeakMap();
class Countdown {
constructor(counter, action) {
_counter.set(this, counter);
_action.set(this, action);
}
dec() {
let counter = _counter.get(this);
if (counter < 1) return;
counter--;
_counter.set(this, counter);
if (counter === 0) {
_action.get(this)();
}
}
}
const c = new Countdown(2, () => console.log('DONE'));
c.dec();
c.dec();
// DONE
```
上面的代码中,Countdown类的两个内部属性 ---_counter 和 _action---是实例的弱引用,如果删除实例,他们也会随之消失,不会造成内存泄漏.
ES6 WeakMap