集合引用类型

Map 结构原生提供三个遍历器生成函数和一个遍历方法

Map.prototype.keys():返回键名的遍历器
Map.prototype.values():返回键值的遍历器
Map.prototype.entries():返回所有成员的遍历器
Map.prototype.forEach():遍历 Map 的所有成员
实例的属性和操作方法
size 属性
Map.prototype.set(key, value) 添加某个值返回 Map 结构本身
Map.prototype.get(key) get方法读取key对应的键值如果找不到key返回undefined
Map.prototype.has(key)返回一个布尔值
Map.prototype.delete(key) 删除某个值返回一个布尔值表示删除是否成功
Map.prototype.clear()清除所有成员没有返回值
WeakMap只有四个方法可用get()、set()、has()、delete()

WeakMap与Map的区别有两点

首先WeakMap只接受对象作为键名null除外),不接受其他类型的值作为键名

WeakMap 就是为了解决这个问题而诞生的它的键名所引用的对象都是弱引用即垃圾回收机制不将该引用考虑在内因此只要所引用的对象的其他引用都被清除垃圾回收机制就会释放该对象所占用的内存也就是说一旦不再需要WeakMap 里面的键名对象和所对应的键值对会自动消失不用手动删除引用

Set 结构的实例有以下属性

Set.prototype.constructor构造函数默认就是Set函数
Set.prototype.size返回Set实例的成员总数
Set 实例的方法分为两大类操作方法用于操作数据和遍历方法用于遍历成员)。下面先介绍四个操作方法

Set.prototype.add(value):添加某个值返回 Set 结构本身
Set.prototype.delete(value):删除某个值返回一个布尔值表示删除是否成功
Set.prototype.has(value):返回一个布尔值表示该值是否为Set的成员
Set.prototype.clear():清除所有成员没有返回值
Set 结构的实例有四个遍历方法可以用于遍历成员

Set.prototype.keys():返回键名的遍历器
Set.prototype.values():返回键值的遍历器
Set.prototype.entries():返回键值对的遍历器
Set.prototype.forEach():使用回调函数遍历每个成员
WeakSet 结构有以下三个方法

WeakSet.prototype.add(value): WeakSet 实例添加一个新成员
WeakSet.prototype.delete(value):清除 WeakSet 实例的指定成员
WeakSet.prototype.has(value):返回一个布尔值表示某个值是否在 WeakSet 实例之中
注意是a数组的成员成为 WeakSet 的成员而不是a数组本身这意味着数组的成员只能是对象

const b = [3, 4];
const ws = new WeakSet(b);
// Uncaught TypeError: Invalid value used in weak set(…)

上面代码试图向 WeakSet 添加一个数值和Symbol值结果报错因为 WeakSet 只能放置对象

其次WeakSet 中的对象都是弱引用即垃圾回收机制不考虑 WeakSet 对该对象的引用也就是说如果其他对象都不再引用该对象那么垃圾回收机制会自动回收该对象所占用的内存不考虑该对象还存在于 WeakSet 之中

这是因为垃圾回收机制根据对象的可达性reachability来判断回收如果对象还能被访问到垃圾回收机制就不会释放这块内存结束使用该值之后有时会忘记取消引用导致内存无法释放进而可能会引发内存泄漏WeakSet 里面的引用都不计入垃圾回收机制所以就不存在这个问题因此WeakSet 适合临时存放一组对象以及存放跟对象绑定的信息只要这些对象在外部消失它在 WeakSet 里面的引用就会自动消失

由于上面这个特点WeakSet 的成员是不适合引用的因为它会随时消失另外由于 WeakSet 内部有多少个成员取决于垃圾回收机制有没有运行运行前后很可能成员个数是不一样的而垃圾回收机制何时运行是不可预测的因此 ES6 规定 WeakSet 不可遍历

WeakSet 不能遍历是因为成员都是弱引用随时可能消失遍历机制无法保证成员的存在很可能刚刚遍历结束成员就取不到了WeakSet 的一个用处是储存 DOM 节点而不用担心这些节点从文档移除时会引发内存泄漏

选择Object还是Map

对于多数 Web 开发任务来说,选择 Object 还是 Map 只是个人偏好问题,影响不大。不过,对于 在乎内存和性能的开发者来说,对象和映射之间确实存在显著的差别。

内存占用

Object 和 Map 的工程级实现在不同浏览器间存在明显差异,但存储单个键/值对所占用的内存数量 都会随键的数量线性增加。批量添加或删除键/值对则取决于各浏览器对该类型内存分配的工程实现。 不同浏览器的情况不同,但给定固定大小的内存,Map 大约可以比 Object 多存储 50%的键/值对。

插入性能

向 Object 和 Map 中插入新键/值对的消耗大致相当,不过插入 Map 在所有浏览器中一般会稍微快 一点儿。对这两个类型来说,插入速度并不会随着键/值对数量而线性增加。如果代码涉及大量插入操 作,那么显然 Map 的性能更佳。

查找速度

与插入不同,从大型 Object 和 Map 中查找键/值对的性能差异极小,但如果只包含少量键/值对, 则 Object 有时候速度更快。在把 Object 当成数组使用的情况下(比如使用连续整数作为属性),浏 览器引擎可以进行优化,在内存中使用更高效的布局。这对 Map 来说是不可能的。对这两个类型而言, 查找速度不会随着键/值对数量增加而线性增加。如果代码涉及大量查找操作,那么某些情况下可能选 择 Object 更好一些。

删除性能

使用 delete 删除 Object 属性的性能一直以来饱受诟病,目前在很多浏览器中仍然如此。为此, 出现了一些伪删除对象属性的操作,包括把属性值设置为undefined或null。但很多时候,这都是一 种讨厌的或不适宜的折中。而对大多数浏览器引擎来说,Map 的 delete()操作都比插入和查找更快。 如果代码涉及大量删除操作,那么毫无疑问应该选择 Map。

Last updated