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会有一些缺点,如下:

  1. Object的属性键是StringSymbol,这限度了它们作为不同数据类型的键/值对汇合的能力
  2. 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) // trueset.delete(2)  set.has(2) // falseset.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) // 1console.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') // truemap.delete('name')  map.has(name) // falsemap.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 18map.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/