带键的集合
Map
Map 对象
Map
对象就是一个简单的键/值对映射集合,可以按照数据插入时的顺序遍历所有的元素。
下面的代码演示了使用 Map
进行的一些基本操作。请参考 Map
以获取更多的样例和完整的 API。你可以使用 for...of
循环来在每一次迭代中得到 [key, value]
数组。
const sayings = new Map();
sayings.set("dog", "woof");
sayings.set("cat", "meow");
sayings.set("elephant", "toot");
sayings.size; // 3
sayings.get("dog"); // woof
sayings.get("fox"); // undefined
sayings.has("bird"); // false
sayings.delete("dog");
sayings.has("dog"); // false
for (const [key, value] of sayings) {
console.log(`${key} goes ${value}`);
}
// "cat goes meow"
// "elephant goes toot"
sayings.clear();
sayings.size; // 0
Object 和 Map 的比较
一般地,object 会被用于将字符串类型映射到值。Object
允许设置键值对、根据键获取值、删除键、检测某个键是否存在。不过,Map
对象还有一些优点,可以更好地应用于映射关系表示中。
Object
的键均为字符串或 Symbol 类型,在Map
里键可以是任意类型。- 必须手动计算
Object
的大小,但是可以很容易地获取Map
的大小(size
)。 Map
的遍历遵循元素的插入顺序。Object
有原型,所以映射中有一些缺省的键。(可以用map = Object.create(null)
回避)。
这三条提示可以帮你决定用 Map
还是 Object
:
- 如果键在运行时才能知道,或者所有的键类型相同,所有的值类型相同,那就使用
Map
。 - 如果需要将原始值存储为键,则使用
Map
,因为Object
将每个键视为字符串,不管它是一个数字值、布尔值还是任何其他原始值。 - 如果存在需要对个别元素进行操作的逻辑,使用
Object
。
WeakMap 对象
WeakMap
是键/值对的集合,其键必须是对象或非注册符号,其值为任意 JavaScript 类型,并且不会对其键创建强引用。也就是说,一个对象作为键出现在 WeakMap
中并不会阻止该对象被垃圾回收。一旦作为键的对象被收集,其在任何 WeakMap
中的相应值也会被垃圾收集,只要它们没有在其他地方被强引用。唯一可用作 WeakMap
键的原始类型类型是 symbol,更具体地说,是非注册 symbol,因为非注册 symbol 保证是唯一的,并且不能被重新创建。
WeakMap
API 与 Map
API 基本相同。不过,WeakMap
不允许观察其键的有效性,这也是它不允许枚举的原因。因此,没有任何方法可以获取 WeakMap
中的键的列表。如果有的话,该列表将取决于垃圾回收的状态,从而引入非确定性。
可以在 WeakMap
参考页的“为什么使用 WeakMap”小节查看更多信息和示例。
WeakMap
对象的一个用例是存储一个对象的私有数据或隐藏实现细节。Nick Fitzgerald 的博文“使用 ECMAScript 6 WeakMap 隐藏实现细节”提供了一个例子。对象内部的私有数据和方法被存储在 WeakMap
类型的 privates
变量中。所有暴露出的原型和实例都是公开的,而其他内容都是外界不可访问的,因为模块并未导出 privates
对象。
const privates = new WeakMap();
function Public() {
const me = {
// 这里是私有数据
};
privates.set(this, me);
}
Public.prototype.method = function () {
const me = privates.get(this);
// 处理 `me` 中的私有数据
// …
};
module.exports = Public;
Set
Set 对象
Set
对象是一组唯一值的集合,可以按照添加顺序来遍历。Set
中的值只能出现一次;它在集合 Set
中是唯一的。
以下代码演示了 Set
的基本操作,更多示例和完整 API 可以参考 Set
。
const mySet = new Set();
mySet.add(1);
mySet.add("some text");
mySet.add("foo");
mySet.has(1); // true
mySet.delete("foo");
mySet.size; // 2
for (const item of mySet) {
console.log(item);
}
// 1
// "some text"
数组和 Set 之间的转换
可以使用 Array.from
或展开语法来完成集合到数组的转换。同样,Set
的构造函数接受数组作为参数,可以完成从 Array
到 Set
的转换。
备注: Set
对象存储唯一值,所以数组转换为 Set 时,任何重复值将会被删除!
Array.from(mySet);
[...mySet2];
mySet2 = new Set([1, 2, 3, 4]);
数组和 Set 的对比
一般情况下,在 JavaScript 中使用数组来存储一组元素,而新的 Set
对象有这些优势:
- 根据值(
arr.splice(arr.indexOf(val), 1)
)删除数组元素效率低下。 Set
对象允许根据值删除元素,而数组中必须使用基于元素下标的splice
方法。- 数组的
indexOf
方法无法找到NaN
值。 Set
对象存储唯一值,所以不需要手动处理包含重复值的情况。
WeakSet 对象
WeakSet
对象是可收集垃圾值的集合,包括对象和非注册 symbol。WeakSet
中的值只能出现一次。它在 WeakSet
的集合中是唯一的。
与 Set
对象的主要区别有:
- 与
Set
相反,WeakSet
是对象或 symbol 的集合,而不是任何类型的任意值的集合。 WeakSet
的弱是指集合中的对象是弱引用的。如果WeakSet
中存储的一个对象不存在其他的引用,那么它就会被垃圾回收。这也意味着集合中不再存储当前对象。WeakSet
不可枚举。
WeakSet
对象的用例有限。它们不会泄露内存,因此可以安全地使用 DOM 元素作为键,并将其标记用于跟踪等目的。
Map 和 Set 的键值相等
Map
对象和 Set
对象的键和值的等值判断都基于 SameValueZero 算法:
- 等价的作用类似于严格相等运算符
===
。 -0
和+0
相等。NaN
与自身相等(与===
有所不同)。