共计 4669 个字符,预计需要花费 12 分钟才能阅读完成。
Set、Map、WeakSet、WeakMap
如果要用一句来形容,咱们能够说
Set 是一种叫做汇合的数据结构,Map 是一种叫做字典的数据结构
那什么是汇合?什么又是字典呢?
- 汇合
汇合,是由一堆无序的、相关联的,且不反复的内存构造【数学中称为元素】组成的组合
- 字典
字典(dictionary)是一些元素的汇合。每个元素有一个称作 key 的域,不同元素的 key 各不相同
那么汇合和字典又有什么区别呢?
- 共同点:汇合、字典都能够存储不反复的值
- 不同点:汇合是以 [值,值] 的模式存储元素,字典是以 [键,值] 的模式存储
背景
大多数支流编程语言都有多种内置的数据汇合。例如 Python
领有列表(list
)、元组(tuple
)和字典(dictionary
),Java 有列表(list
)、汇合(set
)、队列(queue
)
然而 JavaScript
直到 ES6
的公布之前,只领有数组(array
)和对象(object
)这两个内建的数据汇合
在 ES6
之前, 咱们通常应用内置的 Object
模仿 Map
然而这样模仿进去的 map
会有一些缺点,如下:
Object
的属性键是String
或Symbol
,这限度了它们作为不同数据类型的键 / 值对汇合的能力Object
不是设计来作为一种数据汇合,因而没有间接无效的办法来确定对象具备多少属性
Set
定义:
Set
对象容许你存储任何类型的惟一值,无论是原始值或者是对象援用,Set
对象是值的汇合,你能够依照插入的程序迭代它的元素。Set
中的元素只会呈现一次,即Set
中的元素是惟一的
Set
自身是一个构造函数,用来生成 Set
数据结构
根本应用
- 语法
new Set([iterable])
接管一个数组(或者具备 iterable 接口的其余数据结构), 返回一个新的 Set
对象
const set = new Set([1,2,1,2])
console.log(set) // {1,2}
下面代码能够看出 Set
是能够去除数组中的反复元素
属性及办法
属性
- size: 返回汇合中所蕴含的元素的数量
console.log(new Set([1,2,1,2]).size) // 2
操作方法
- add(value): 向汇合中增加一个新的项
- delete(value): 从汇合中删除一个值
- has(value): 如果值在汇合中存在,返回 ture, 否则返回 false
- clear(): 移除汇合中的所有项
let set = new Set()
set.add(1)
set.add(2)
set.add(2)
set.add(3)
console.log(set) // {1,2,3}
set.has(2) // true
set.delete(2)
set.has(2) // false
set.clear()
遍历办法
- keys(): 返回键名的遍历器
- values(): 返回键值的遍历器
- entries(): 返回键值对的遍历器
- forEach(): 应用回调函数遍历每个成员
let set = new Set([1,2,3,4])
// 因为 set 只有键值,没有键名,所以 keys() values()行为完全一致
console.log(Array.from(set.keys())) // [1,2,3,4]
console.log(Array.from(set.values())) // [1,2,3,4]
console.log(Array.from(set.entries())) // [[1,1],[2,2],[3,3],[4,4]]
set.forEach((item) => {console.log(item)}) // 1,2,3,4
利用场景
因为 Set
构造的值是惟一的,咱们能够很轻松的实现以下
// 数组去重
let arr = [1, 1, 2, 3];
let unique = [... new Set(arr)];
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 并集
let union = [...new Set([...a, ...b])]; // [1,2,3,4]
// 交加
let intersect = [...new Set([...a].filter(x => b.has(x)))]; [2,3]
// 差集
let difference = Array.from(new Set([...a].filter(x => !b.has(x)))); [1]
WeakSet
WeakSet 对象是一些对象值的汇合, 并且其中的每个对象值都只能呈现一次。在 WeakSet 的汇合中是惟一的
WeakSet
的呈现次要解决弱援用对象存储的场景, 其构造与 Set
相似
与 Set
的区别
- 与 Set 相比,WeakSet 只能是对象的汇合,而不能是任何类型的任意值
- WeakSet 汇合中对象的援用为弱援用。如果没有其余的对 WeakSet 中对象的援用,那么这些对象会被当成垃圾回收掉。这也意味着 WeakSet 中没有存储以后对象的列表。正因为这样,WeakSet 是不可枚举的
WeakSet
的属性跟操作方法与 Set
统一,不同的是 WeakSet
没有遍历办法,因为其成员都是弱援用,弱援用随时都会隐没,遍历机制无奈保障成员的存在
下面始终有提到弱援用,那弱援用到底是指什么呢?
弱援用是指不能确保其援用的对象不会被垃圾回收器回收的援用,换句话说就是可能在任意工夫被回收
Map
Map 对象保留键值对,并且可能记住键的原始插入程序。任何值 (对象或者原始值) 都能够作为一个键或一个值。一个 Map 对象在迭代时会依据对象中元素的插入程序来进行 — 一个 for…of 循环在每次迭代后会返回一个模式为[key,value] 的数组
根本应用
- 语法
new Map([iterable])
Iterable
能够是一个数组或者其余 iterable
对象,其元素为键值对(两个元素的数组,例如: [[ 1, ‘one’],[2, ‘two’]])。每个键值对都会增加到新的 Map
let map = new Map()
map.set('name', 'vuejs.cn');
console.log(map.get('name'))
属性及办法
根本跟 Set
相似,同样具备如下办法
属性
- size: 返回 Map 构造的元素总数
let map = new Map()
map.set('name', 'vuejs.cn');
console.log(map.size) // 1
console.log(new Map([['name','vue3js.cn'], ['age','18']]).size) // 2
操作方法
- set(key, value): 向 Map 中退出或更新键值对
- get(key): 读取 key 对用的值,如果没有,返回 undefined
- has(key): 某个键是否在 Map 对象中,在返回 true 否则返回 false
- delete(key): 删除某个键,返回 true, 如果删除失败返回 false
- clear(): 删除所有元素
let map = new Map()
map.set('name','vue3js.cn')
map.set('age','18')
console.log(map) // Map {"name" => "vuejs.cn", "age" => "18"}
map.get('name') // vue3js.cn
map.has('name') // true
map.delete('name')
map.has(name) // false
map.clear() // Map {}
遍历办法
- keys():返回键名的遍历器
- values():返回键值的遍历器
- entries():返回所有成员的遍历器
- forEach():遍历 Map 的所有成员
let map = new Map()
map.set('name','vue3js.cn')
map.set('age','18')
console.log([...map.keys()]) // ["name", "age"]
console.log([...map.values()]) // ["vue3js.cn", "18"]
console.log([...map.entries()]) // [['name','vue3js.cn'], ['age','18']]
// name vuejs.cn
// age 18
map.forEach((value, key) => {console.log(key, value)})
利用场景
Map
会保留所有元素的程序, 是在基于可迭代的根底上构建的,如果思考到元素迭代或程序保留或键值类型丰盛的状况下都能够应用,上面摘抄自 vue3
源码中依赖收集的外围实现
let depsMap = targetMap.get(target)
if (!depsMap) {targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key)
if (!dep) {depsMap.set(key, (dep = new Set()))
}
if (!dep.has(activeEffect)) {dep.add(activeEffect)
activeEffect.deps.push(dep)
...
}
WeakMap
WeakMap 对象是一组键 / 值对的汇合,其中的键是弱援用的。其键必须是对象,而值能够是任意的
与 Map
的区别
- Map 的键能够是任意类型,WeakMap 的键只能是对象类型
- WeakMap 键名所指向的对象,不计入垃圾回收机制
WeakMap
的属性跟操作方法与 Map
统一,同 WeakSet
一样,因为是弱援用,所以 WeakSet
也没有遍历办法
类型的转换
Map
转为Array
// 解构
const map = new Map([[1, 1], [2, 2], [3, 3]])
console.log([...map]) // [[1, 1], [2, 2], [3, 3]]
// Array.from()
const map = new Map([[1, 1], [2, 2], [3, 3]])
console.log(Array.from(map)) // [[1, 1], [2, 2], [3, 3]]
Array
转为Map
const map = new Map([[1, 1], [2, 2], [3, 3]])
console.log(map) // Map {1 => 1, 2 => 2, 3 => 3}
Map
转为Object
// 非字符串键名会被转换为字符串
function mapToObj(map) {let obj = Object.create(null)
for (let [key, value] of map) {obj[key] = value
}
return obj
}
const map = new Map().set('name', 'vue3js.cn').set('age', '18')
mapToObj(map) // {name: "vue3js.cn", age: "18"}
Object
转为Map
let obj = {"a":1, "b":2};
let map = new Map(Object.entries(obj))
总结
- Set、Map、WeakSet、WeakMap、都是一种汇合的数据结构
- Set、WeakSet 是 [值, 值] 的汇合,且具备唯一性
- Map 和 WeakMap 是一种 [键, 值] 的汇合,Map 的键能够是任意类型,WeakMap 的键只能是对象类型
- Set 和 Map 有遍历办法,WeakSet 和 WeakMap 属于弱援用不可遍历
参考
- https://zh.wikipedia.org/wiki/%E5%BC%B1%E5%BC%95%E7%94%A8
- https://developer.mozilla.org/
- https://es6.ruanyifeng.com/