07 Map

76 阅读5分钟
│   ├── Map
│   │     └─ 含义和基本用法
│   │         └─ JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。
│   │         └─ 为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,MapObject 更合适。
│   │         └─ 如果对同一个键多次赋值,后面的值将覆盖前面的值。
│   │         └─ Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。这就解决了同名属性碰撞(clash)的问题,我们扩展别人的库的时候,如果使用对象作为键名,就不用担心自己的属性与原作者的属性同名。
│   │         └─ 如果 Map 的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map 将其视为一个键,比如`0``-0`就是一个键,布尔值`true`和字符串`true`则是两个不同的键。另外,`undefined``null`也是两个不同的键。虽然`NaN`不严格相等于自身,但 Map 将其视为同一个键。
│   │     └─ 实例的属性和操作方法
│   │         └─ (1)size 属性
│   │             └─ `size`属性返回 Map 结构的成员总数。
│   │         └─ (2Map.prototype.set(key, value)
│   │             └─ `set`方法设置键名`key`对应的键值为`value`,然后返回整个 Map 结构。如果`key`已经有值,则键值会被更新,否则就新生成该键。`set`方法返回的是当前的`Map`对象,因此可以采用链式写法。
│   │         └─ (3Map.prototype.get(key)
│   │             └─ `get`方法读取`key`对应的键值,如果找不到`key`,返回`undefined`。
│   │         └─ (4Map.prototype.has(key)
│   │             └─ `has`方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
│   │         └─ (5Map.prototype.delete(key)
│   │             └─ `delete`方法删除某个键,返回`true`。如果删除失败,返回`false`。
│   │         └─ (6Map.prototype.clear()
│   │             └─ `clear`方法清除所有成员,没有返回值。
│   │     └─ 遍历方法
│   │         └─ - `Map.prototype.keys()`:返回键名的遍历器。
│   │         └─ - `Map.prototype.values()`:返回键值的遍历器。
│   │         └─ - `Map.prototype.entries()`:返回所有成员的遍历器。
│   │         └─ - `Map.prototype.forEach()`:遍历 Map 的所有成员。
│   │         └─ Map 结构转为数组结构,比较快速的方法是使用扩展运算符(`...`)。
│   │     └─ 与其他数据结构的互相转换
│   │         └─ (1Map 转为数组
│   │             └─ 前面已经提过,Map 转为数组最方便的方法,就是使用扩展运算符(`...`)。
│   │         └─ (2)数组 转为 Map
│   │             └─ 将数组传入 Map 构造函数,就可以转为 Map。
│   │         └─ (3Map 转为对象
│   │             └─ 如果所有 Map 的键都是字符串,它可以无损地转为对象。
│   │         └─ (4)对象转为 Map
│   │             └─ 对象转为 Map 可以通过`Object.entries()`。
│   │         └─ (5Map 转为 JSON
│   │             └─ Map 转为 JSON 要区分两种情况。一种情况是,Map 的键名都是字符串,这时可以选择转为对象 JSON。
│   │             └─ 另一种情况是,Map 的键名有非字符串,这时可以选择转为数组 JSON。
│   │         └─ (6JSON 转为 Map
│   │             └─ JSON 转为 Map,正常情况下,所有键名都是字符串。
│   ├── WeakMap
│   │     └─ 含义
│   │         └─ `WeakMap`结构与`Map`结构类似,也是用于生成键值对的集合。
│   │         └─ `WeakMap``Map`的区别有两点。
│   │             └─ 首先,`WeakMap`只接受对象作为键名(`null`除外),不接受其他类型的值作为键名。
│   │             └─ 其次,`WeakMap`的键名所指向的对象,不计入垃圾回收机制。
│   │         └─ `e1``e2`是两个对象,我们通过`arr`数组对这两个对象添加一些文字说明。这就形成了`arr``e1``e2`的引用。一旦不再需要这两个对象,我们就必须手动删除这个引用,否则垃圾回收机制就不会释放`e1``e2`占用的内存。WeakMap 就是为了解决这个问题而诞生的,它的键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内。因此,只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。基本上,如果你要往对象上添加数据,又不想干扰垃圾回收机制,就可以使用 WeakMap。一个典型应用场景是,在网页的 DOM 元素上添加数据,就可以使用`WeakMap`结构。当该 DOM 元素被清除,其所对应的`WeakMap`记录就会自动被移除。
│   │     └─ WeakMap 的语法
│   │         └─ WeakMapMapAPI 上的区别主要是两个,一是没有遍历操作(即没有`keys()``values()``entries()`方法),也没有`size`属性。因为没有办法列出所有键名,某个键名是否存在完全不可预测,跟垃圾回收机制是否运行相关。这一刻可以取到键名,下一刻垃圾回收机制突然运行了,这个键名就没了,为了防止出现不确定性,就统一规定不能取到键名。二是无法清空,即不支持`clear`方法。因此,`WeakMap`只有四个方法可用:`get()``set()``has()``delete()`。
│   │     └─ WeakMap 的示例
│   │         └─ WeakMap 的例子很难演示,因为无法观察它里面的引用会自动消失。此时,其他引用都解除了,已经没有引用指向 WeakMap 的键名了,导致无法证实那个键名是不是存在。
│   │     └─ WeakMap 的用途
│   │         └─ WeakMap 应用的典型场合就是 DOM 节点作为键名。下面是一个例子。
│   ├── WeakRef
│   │     └─ WeakSetWeakMap 是基于弱引用的数据结构 更进一步,提供了 WeakRef 对象,用于直接创建对象的弱引用。
│   │     └─ `target`是原始对象,构造函数`WeakRef()`创建了一个基于`target`的新对象`wr`。这里,`wr`就是一个 WeakRef 的实例,属于对`target`的弱引用,垃圾回收机制不会计入这个引用,也就是说,`wr`的引用不会妨碍原始对象`target`被垃圾回收机制清除。
│   │     └─ WeakRef 实例对象有一个`deref()`方法,如果原始对象存在,该方法返回原始对象;如果原始对象已经被垃圾回收机制清除,该方法返回`undefined`