一.集合的作用与特点
集合包括Set/WeakSet、Map/WeakMap,类似于hash table。特点如下:
避免了对象用作hash table时的弊端(为了避免原型属性,可能需要用
Object.create(null)
来创建原型为null
的空对象,而不是字面量{}
,因为{} === Object.create(Object.prototype)
)消除了用户数据和内置方法的冲突,不把数据作为属性暴露出来,无法通过
.key
或者[key]
访问数据,而且集合不会从原型链继承key
集合遍历顺序与插入顺序相同(而且可以利用
for...of
等迭代器相关的新特性),而hash table理论上遍历顺序应该是不确定的(取决于hash函数)支持
has()
方法,用于包含性检测,比indexOf更快
二.Set
unique值集。语法如下:
// 创建,参数iterable可选
new Set(iterable)
// 增,value可以是任意类型的
Set.prototype.add(value)
// 删|清空
Set.prototype.delete(value)|Set.prototype.clear()
// 查
Set.prototype.has(value)
// 遍历,callback(value, value, set)
Set.prototype.forEach(callbackFn[, thisArg])
// 获取元素个数
Set.prototype.size
// 获取各种迭代器,为了兼容Map,Set中keys === values
set.keys()、set.values()和set.entries()
特点:
自动去重,重复值无法add,但2个属性相同的对象被认为是不重复的
1行代码完成数组去重:
Array.from(new Set(arr))
避免了用户数据和内置方法的冲突,不把数据作为属性暴露出来,无法通过
.key
或者[key]
访问数据,而且集合不会从原型链继承key
示例:
var set = new Set('12231');
// 自动去重
console.log(set); // Set {"1", "2", "3"}
// 重复值无法add
set.add('1');
console.log(set); // Set {"1", "2", "3"}
set.add(1);
console.log(set); // Set {"1", "2", "3", 1}
set.add({a: 1});
// 不重复
set.add({a: 1});
console.log(set); // Set {"1", "2", "3", 1, Object {a: 1}, Object {a: 1}}
两个属性值完全相同的对象会被认为是不重复的(当然,两个指向相同对象的引用是重复的),而且Set内部的hash函数不支持重写(安全性考虑),无法改变这种行为
三.Map
键值对集。语法如下:
// 创建,参数pairs可选,pairs可以是[[key, val], ]二维数组、现有Map等等
new Map(pairs)
// 增|改/删|清空/查找/读取
map.set(key, val)/map.delete(key)|map.clear()/map.has(key)/map.get(key)
// 遍历,callback(value, key, set),注意参数顺序
map.forEach(callback)
// 获取元素个数
Map.prototype.size
// 获取各种迭代器,遍历键/值/键值对
map.keys()、map.values()和map.entries()
特点:key
可以是任意类型,包括Object(不像对象的key
只能是String或者Symbol)
示例:
var map = new Map([['a', 1], ['b', 2]]);
//!!! 注意参数顺序
map.forEach((val, key, arr) => {
console.log(`val = ${val}, key = ${key}`);
});
// log print:
// val = 1, key = a
// val = 2, key = b
// val = objA, key = [object Object]
注意:map.forEach参数顺序为callback(value, key, set),而不是与Array.forEach类似的callback(key, value, set)
四.WeakSet/WeakMap
功能受限的弱引用集合。避免Set/Map强引用带来的内存泄漏问题,比如set.add(domNode)
之后domNode
被remove
了,gc无法回收domNode
对象,因为set
是强引用,只有调用set.delete(domNode)
后才能回收内存
特点:
WeakSet只支持new, add, delete, has
WeakMap只支持new, get, set, delete, has
WeakSet的值和WeakMap的键必须是Object
不支持迭代
gc可以回收仍在使用中的Weak集里的无效元素
P.S.第3条比较奇怪(可能是出于gc考虑)
总之,在可能存在内存泄漏的场景(比如频繁DOM操作,复杂动画等等),考虑采用弱集合
五.总结
1行代码完成数组去重,多少让人有那么点激动
手捏数据结构的日子正在慢慢过去,底层建筑正在完善
参考资料
- 《ES6 in Depth》:InfoQ中文站提供的免费电子书
查询单个元素是否存在,这两个都是o(1)
hash table