学习链接
Map、Set、WeakMap、WeakSet、WeakRef
- 对象,存储带有键的数据的集合。
- 数组,存储有序集合。
- Map,类似对象,但是允许任何类型的键
- Set,类似数组,但是成员值唯一
Map 对比 Object
Map 是一个带键的数据项的集合。
不同于对象只允许使用字符串作为键,Map 允许任何类型的键(key)。
Set 对比 Array
Set 是一个特殊的类型集合 —— “值的集合”(没有键),它的每一个值只能出现一次。
对比数组方法去重,在每次插入值的时候检查数组元素是否重复,但是这样性能很差,因为目前的数组方法会遍历整个数组来检查重复元素。
Set 内部对唯一性检查进行了优化。
Map/Set 键的迭代顺序
在 Map 和 Set 中迭代总是按照值插入的顺序进行的。
所以这些集合不是无序的,但是无法对元素进行重新排序,也不能直接按其编号来获取元素。
Map/Set 键的比较
Map/Set 使用 SameValueZero 算法来比较键是否相等。
它和严格等于 === 的区别是 NaN 被看成是等于 NaN。所以 NaN 也可以被用作键。
WeakMap/WeakSet 对比 Map/Set
使用目的:防止内存泄漏
垃圾回收机制不考虑 WeakMap键名 和 WeakSet 对于对象的引用。
也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakMap键名 和 WeakSet 之中。
这是因为垃圾回收机制根据对象的可达性(reachability)来判断回收,如果对象还能被访问到,垃圾回收机制就不会释放这块内存。
结束使用该值之后,有时会忘记取消引用,导致内存无法释放,进而可能会引发内存泄漏。
WeakMap键名 和 WeakSet 里面的引用,都不计入垃圾回收机制,所以就不存在这个问题。
// Maplet john = { name: "John" };let map = new Map();map.set(john, "...");john = null; // 覆盖引用// john 被存储在了 map 中,// 此时仍然可以使用 map.keys() 来获取它// WeakMaplet john = { name: "John" };let weakMap = new WeakMap();weakMap.set(john, "..."); // 此处引用不被计入垃圾回收机制john = null; // 覆盖引用// john 后续将会被垃圾回收机制回收
只收非 null 对象
WeakMap只接受对象作为键名(不接受null)WeakSet只接受对象作为成员(不接受null)
不可遍历
不可遍历,无
clear()无
size属性,内部的成员个数取决于垃圾回收机制是否运行
WeakRef
WeakSet 和 WeakMap 是基于弱引用的数据结构,ES2021 更进一步,提供了 WeakRef 对象,用于直接创建对象的弱引用。
let target = {};let wr = new WeakRef(target);
wr 就是一个 WeakRef 的实例,属于对 target 的弱引用,垃圾回收机制不会计入这个引用。
也就是说,wr 的引用不会妨碍原始对象 target 被垃圾回收机制清除。
WeakRef.prototype.deref()
如果原始对象存在,该方法返回原始对象;
如果原始对象已经被垃圾回收机制清除,该方法返回
undefined。
注意,标准规定,一旦使用 WeakRef() 创建了原始对象的弱引用,那么在本轮事件循环(event loop),原始对象肯定不会被清除,只会在后面的事件循环才会被清除。
Map 映射
Map —— 是一个带键的数据项的集合。
操作方法和属性
new Map([iterable])—— 创建 map,可选择带有[key,value]对的iterable(例如数组)来进行初始化。map.set(key, value)—— 根据键存储值,返回 map 自身。map.get(key)—— 根据键来返回值,如果map中不存在对应的key,则返回undefined。map.has(key)—— 如果key存在则返回true,否则返回false。map.delete(key)—— 删除指定键对应的值,如果在调用时key存在,则返回true,否则返回false。map.clear()—— 清空 map 。map.size—— 返回当前元素个数。
遍历方法
map.keys()—— 遍历并返回一个包含所有键的可迭代对象map.values()—— 遍历并返回一个包含所有值的可迭代对象map.entries()—— 遍历并返回一个包含所有实体[key, value]的可迭代对象,for..of在默认情况下使用的就是这个map.forEach((value, key, map) => {})
Object.entries:从对象创建 Map
当创建一个 Map 后,我们可以传入一个带有键值对的数组(或其它可迭代对象)来进行初始化。
Object.entries(obj) 方法返回对象的键/值对数组,该数组格式完全按照 Map 所需的格式。
// 键值对 [key, value] 数组const map = new Map([['1', 'str1'],[true, 'bool1']]);// 对象的键值对数组let obj = {name: "John",age: 30};const map = new Map(Object.entries(obj));
Object.fromEntries:从 Map 创建对象
Object.fromEntries 方法根据一个具有 [key, value] 键值对的数组创建一个对象。
const prices = Object.fromEntries([['banana', 1],['orange', 2],['meat', 4]]);// prices 为 { banana: 1, orange: 2, meat: 4 }const map = new Map();map.set('banana', 1);map.set('orange', 2);const obj = Object.fromEntries(map.entries()); // 创建一个普通对象(plain object)// 或者const obj = Object.fromEntries(map);// obj 为 { banana: 1, orange: 2}
Set 集合
Set —— 是一组唯一值的集合。
操作方法和属性
new Set([iterable])—— 创建 set,可选择带有iterable(例如数组)来进行初始化。set.add(value)—— 添加一个值(如果value存在则不做任何修改),返回 set 本身。set.delete(value)—— 删除值,如果value在这个方法调用的时候存在则返回true,否则返回false。set.has(value)—— 如果value在 set 中,返回true,否则返回false。set.clear()—— 清空 set。set.size—— 元素的个数。
遍历方法
set.keys()—— 遍历并返回一个包含所有值的可迭代对象set.values()—— 与set.keys()作用相同,这是为了兼容Map,for..of在默认情况下使用的就是这个set.entries()—— 遍历并返回一个包含所有的实体[value, value]的可迭代对象,它的存在也是为了兼容Mapset.forEach((value, valueAgain, set) => {}),同样为了兼容Map
WeakMap 弱映射
只支持 get,set,has 和 delete 方法。
WeakSet 弱集合
支持 add,has 和 delete 方法,
WeakRef 弱引用
WeakSet 和 WeakMap 是基于弱引用的数据结构,ES2021 更进一步,提供了 WeakRef 对象,用于直接创建对象的弱引用。
let target = {};let wr = new WeakRef(target);
上面示例中,target 是原始对象,构造函数 WeakRef() 创建了一个基于 target 的新对象 wr。这里,wr 就是一个 WeakRef 的实例,属于对 target 的弱引用,垃圾回收机制不会计入这个引用,也就是说,wr 的引用不会妨碍原始对象 target 被垃圾回收机制清除。
WeakRef 实例对象有一个 deref() 方法,如果原始对象存在,该方法返回原始对象;如果原始对象已经被垃圾回收机制清除,该方法返回 undefined。
let target = {};let wr = new WeakRef(target);let obj = wr.deref();if (obj) { // target 未被垃圾回收机制清除// ...}
上面示例中,deref()方法可以判断原始对象是否已被清除。
弱引用对象的一大用处,就是作为缓存,未被清除时可以从缓存取值,一旦清除缓存就自动失效。
注意,标准规定,一旦使用WeakRef()创建了原始对象的弱引用,那么在本轮事件循环(event loop),原始对象肯定不会被清除,只会在后面的事件循环才会被清除。
