# Set 和 Map
# Set
Set 类型是一种有序列表,其中含有一些相互独立的非重复值,通过 Set 集合可以快速访问其中的数据,更有效地追踪各种离散值。
在 Set 集合中,不会对所存值进行强制类型转换,数字 5 和字符串 "5" 可以作为两个独立元素存在(引擎内部使用 Object.is()
方法检测两个值是否一致,唯一的例外是,Set 集合中的 +0 和 -0 被认为是相等的),当然,如果向 Set 集合中添加多个对象,则它们之间彼此保持独立。
# Set 的一些特性
- Set 的方法和属性包括:
add()
delete()
clear()
has()
keys()
values()
entries()
forEach()
size
属性,keys()
values()
entries()
返回的都是一个迭代器对象 - 如果多次调用
add()
方法并传入相同的值作为参数,那么后续的调用实际上会被忽略。 - Set 构造函数可以接受所有可迭代对象作为参数,数组、Set 集合、Map 集合,DOM 中的 NodeList 对象、String 对象,函数的 arguments 属性,都是可迭代的,因而都可以作为 Set 构造函数的参数使用;构造函数通过迭代器从参数中提取值。
- 通过
has()
方法可以检测 Set 集合中是否存在某个值。之前在对象中都是读取对象的值来判断属性是否存在,但是在 JavaScript 中有一个in
运算符,其不需要读取对象的值就可以判断属性在对象中是否存在,如果存在就返回 true。但是in
运算符也会检索对象的原型,只有当对象原型为null
时使用这个方法才比较稳妥。即便是这样,也建议使用in
运算符,不过实际开发中许多开发者仍然使用访问属性的方法进行判断,并没有使用 in 运算符。 - Set 集合的
forEach()
方法比较特别,回调函数的前两个参数的值竟然是一样的。尽管这看起来像是一个错误,不过也解释的通。数组和 Map 集合的forEach()
的回调函数都接受 3 个参数,前两个分别是值和键名(对于数组来说就是数值型索引值),然而 Set 集合没有键名,ECMAScript 6 标准制定委员会本可以规定 Set 集合的forEach()
的回调函数只接受两个参数,但这可能导致几个方法之间分歧过大,于是他们最终决定所有函数都接受 3 个参数:Set 集合中的每个元素也按照键名和值的形式存储,从而才能保证在所有forEach()
方法的回调函数中前两个参数值具有相同含义。 - 尽管 Set 集合更适合用来跟踪多个值,而且又可以通过 forEach() 方法操作集合中的每一个元素,但是不能像访问数组元素那样直接通过索引访问集合中的元素。如有需要,最好先将 Set 集合转换成一个数组,方法可以用展开运算符
...
或者Array.from()
展开运算符(展开语法)
...
,可以在函数调用/数组构造时,将数组表达式或者 string 在语法层面展开(其他的可迭代对象也可以),还可以在构造字面量对象时,将对象表达式按 key-value 的方式展开。
# Weak Set
- Set 类型可以被看作是一个强引用的 Set 集合,Weak Set 集合是弱引用的 Set 集合。Weak Set 集合只存储对象的弱引用,并且不可以存储原始值;集合中的弱引用如果是对象唯一的引用,则会被垃圾回收并释放相应内存。
- Weak Set 集合支持 3 个方法:
add()
、has()
、delete()
- Weak Set 不接受任何原始值,如果 WeakSet 构造函数传入的数组中包含其他非对象值,程序会抛出错误,如果在 WeakSet 实例中,向
add()
方法传入非对象参数会导致程序报错,而向has()
和delete()
方法传入非对象参数则会返回 false - Weak Set 集合不可迭代,所以不能被用于 for-of 循环,也不支持
forEach()
方法 - Weak Set 集合不暴露任何迭代器(例如
keys()
和values()
方法),所以无法通过程序本身来检测其中的内容 - Weak Set 集合不支持 size 属性
- Weak Set 集合的功能看似受限,其实这是为了让它能够正确地处理内存中的数据。总之,如果只需要跟踪对象引用,更应该使用 Weak Set 集合而不是普通的 Set 集合
# 实用
Set 集合中都是相互对立的非重复值,所以可以用于数组去重
function eliminateDuplicates(items) {
return [...new Set(items)]
}
# Map
ECMAScript 6 中的 Map 类型是一种存储着许多键值对的有序列表,其中的键名和对应的值支持所有的数据类型。键名的等价性判断是通过调用 Object.is() 方法实现的,所以数字 5
与字符串 "5"
会被判定为两种类型,可以分别作为独立的两个键。
# Map 的一些特性
- Map 的方法和属性:
set()
get()
delete()
clear()
has()
keys()
values()
entries()
forEach()
size
属性 - 初始化 Map 构造函数时,需要用到二维数组。数组包裹数组的模式看起来可能有点奇怪,但由于 Map 集合可以接受任意数据类型的键名,为了确保他们在被存储到 Map 集合之前不会被强制转换为其他数据类型,因而只能将它们放在数组中,因为这是唯一一种可以准确地呈现键名类型的方式。
# Weak Map
- ECMAScript 6 中的 Weak Map 类型是一种存储着许多键值对的无序列表,列表的键名必须是非
null
类型的对象,键名对应的值则可以是任意类型 - Weak Map 是弱引用 Map 集合,用于存储对象的弱引用,集合中保存这些对象的弱引用,如果在弱引用之外不存在其他的强引用,引擎的垃圾回收机制会自动回收这个对象,同时也会移除 Weak Map 集合中的键值对,但是只有集合的键名遵从这个规则,键名对应的值如果是一个对象,则保存的是对象的强引用,不会触发垃圾回收机制。
- Weak Map 集合中的键名必须是一个对象,如果使用非对象键名会报错
- Weak Map 集合只支持 4 个可以操作键值对的方法:
set()
get()
has()
delete()