Map Set WeakMap WeakSet 场景

1,904 阅读5分钟

Set 和 Map 是 ES6 新增的数据结构。成员的值都是唯一的、无重复的。

1、Set 元素唯一

Set 对象是值的集合,且元素是唯一的,类比数组。

留意下: 唯一值的判断和 === 的运算符算法是不一样的。-0 +0 是两个值,-0\+0 是相同的。NaN之间视为相同的值,

consturctor 上的api

  • add 返回Set对象,所以可以链式添加 xx.add().add(3) 添加元素
  • delete 返回Boolean 结果
  • 这里的entries 返回一个新的迭代器对象,每个键和值相等。
  • Set.prototype.forEach(callbackFn[, thisArg]) 如果提供了thisArg参数,回调中的this会是这个参数。

有迭代器对象的,都可以用for ... of 进行遍历

for (let item of mySet) console.log(item);
// key === value 每个键和值相等
for(let [key, value] of mySet.entries()) console.log(key, value)

set 和Array 的互换

let set = new Set([1,2,3])
[...set] // [1,2,3]
Array.from(set)

求交集、并集、差集, 数组去重

// 交集
let intersection = new Set([...set1].filter(x => set2.has(x)))
let union = new Set([...set1, ...set2])
let difference = new Set([...set1].filter(x => !set2.has(x)))

如果Set 传入一个字符串,则会拆开 //

Array.from(new Set('Hello')) // ['H', 'e', 'l', 'l', 'o']

如何修改?

2、Map 保存键值对,且有插入顺序

Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值) 都可以作为一个键或一个值。 键值相等的判断和set 上是一致的。

Map 和 Object 的比较:

对比选项MapObject
键名冲突Map默认没有显式的键容易与原型的键名产生冲突
键的类型Map的键可以是任意值,函数、对象或任意基本类型object 必须是string 或 symbol
size返回键值对数量object keys 读取
迭代Map 是 iterable 的,可以直接被迭代(for...of)。for...in or keys遍历
性能频繁增删键值对的场景下表现更好

constructor 上的api

  • 通过set 给Map对象新增键值。返回Map对象,可链式
let myMap = new Map()
let keyObj = {}
myMap.set(keyObj, '新增键值')
myMap.get(keyObj) // 新增键值
myMap.get({}) // undefined
myMap.get(NaN); // NaN 是一个
// 注意是value key o
myMap.forEach(function(value, key) {
  console.log(key + " = " + value);
})

Map与数组的关系

二维数组的形式

let arr = [[key, value], [xiaoming, 12]]
let aMap = new Map(arr)
let arr2 = [...aMap]
console.log(Array.from(aMap.keys))
// Map对象同数组进行合并时,如果有重复的键值,则后面的会覆盖前面的。
let merged = new Map([...arr, ...xxx, [1, 'eins']]);

通过map.set 进行值覆盖。

注意!别为Map设置对象属性,而应该用set。不然会有意外。 let wrongMap = new Map() wrongMap['bla'] = 'blaa'

3、WeakSet 只能是对象的集合,不能是任何类型的任意值、无法枚举

WeakSet 对象集合中对象的引用为弱引用,不会被标记引用,容易被垃圾回收。

递归、涉及较多对象时,使用。

垃圾回收案例:

let obj = {name : 'hhh'}
// 如果新增 let array = [obj] 则不会被垃圾回收
// let map = new WeakMap()  map.set(obj, '这样也是会被垃圾回收,因为是弱引用')
obj = null // 可以被回收

ECMAScript 6: what is WeakSet for?

案例:

案例1、我们可以将用户添加到 WeakSet 中,以追踪访问过我们网站的用户

let visitedSet = new WeakSet();

let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };

visitedSet.add(john); // John 访问了我们
visitedSet.add(pete); // 然后是 Pete
visitedSet.add(john); // John 再次访问

// visitedSet 现在有两个用户了
// 检查 John 是否来访过?
alert(visitedSet.has(john)); // true
// 检查 Mary 是否来访过?
alert(visitedSet.has(mary)); // false
john = null;
// visitedSet 将被自动清理

案例2、

const requests = new WeakSet();
class ApiRequest {
  constructor() {
    requests.add(this);
  }
  makeRequest() {
    if(!request.has(this)) throw new Error("Invalid access");
    // do work
  }
}
// 如果没有weakSet ,你可能需要 通过生命周期去管理,手动删除
const requests = new Set();
class ApiRequest {
  constructor() {
    requests.add(this);
  }
  makeRequest() {
    if(!request.has(this)) throw new Error("Invalid access");
    // do work
  }
  destory(){
    requests.delete(this)
  }
}

ApiRequest 类中想验证 this 对象的来源,于是需要一个集合来存所有通过构造函数构建的对象,ApiRequest 类却并不像参与到实例对象的生命周期中去,直接用 Set 的话,由于Set 对于实例对象存在引用,就会发生内存泄漏。

案例3、DOM节点作为键名

consturctor 上的api

4、WeakMap 键必须是对象、无法枚举

WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。 Map 的赋值和搜索操作都是O(n)、且容易导致内存泄漏,因为数组会一直引用着每个键和值。

基本上,如果你要往对象上添加数据,即额外数据的存储 又不想干扰垃圾回收机制,就可以使用 WeakMap。

用来存这个对象相关的数据,与数据共存亡:

  • 案例1、一个用户对象作为键,其访问次数为值。当一个用户离开时(该用户对象将被垃圾回收机制回收),这时我们就不再需要他的访问次数了
let visitsCountMap = new WeakMap()

// 递归用户来访次数
function countUser(user){
	let count = visitsCountMap.get(user) || 0
    visitsCountMap.set(user, count + 1)
}

// 📁 main.js
let john = { name: "John" };
countUser(john); // count his visits
// 不久之后,john 离开了
john = null;
  • 案例2、缓存计算的结果
let cache = new WeakMap()

// 与obj 嘻嘻相关的结果
function process(obj){
	if(!cache.has(obj)) {
    	let result = `与obj有关的计算`
        cache.set(obj, result)
    }
    return cache.get(obj)
}

// other.js
let obj = {}
let result1 = process(obj)
let result2 = process(obj)
obj = null // 如果是Map 就cache 里不可被回收

案例3、DOM节点作为键名

consturctor 上的api

小汇:

  • Set 类似于数组,成员值唯一。
  • WeakSet 类似 Set,但成员只能是对象,且没有遍历操作。不引用后会被自动回收。
  • Map 类似于对象,key值不限于字符串,成员值唯一。
  • WeakMap 类似 Map,但只接受对象作为键名(null除外),且没有遍历操作。不引用后会被回收。

通过垃圾回收机制来了解Map与WeakMap

MDN