Set

在ES6之前,我们存储数据的结构主要有两种:数组、对象。
在ES6中新增了另外两种数据结构:Set、Map,以及它们的另外形式WeakSet、WeakMap。

Set是一个新增的数据结构,可以用来保存数据,类似于数组,但是和数组的区别是元素不能重复

创建 Set 我们需要通过 Set 构造函数(暂时没有字面量创建的方式):

我们可以发现 Set 中存放的元素是不会重复的,那么 Set 有一个非常常用的功能就是给数组去重

  1. // 1.创建Set结构
  2. const set = new Set()
  3. set.add(10)
  4. set.add(20)
  5. set.add(10) // 重复数据添加不上
  6. // 2.添加对象时特别注意:
  7. set.add({})
  8. set.add({}) // 这是两个对象,添加成功,因为内存地址不一样
  9. const obj = { name: 'zs'}
  10. set.add(obj)
  11. set.add(obj) // 这是同一个对象,添加失败
  12. console.log(set) // Set(5) { 10, 20, {}, {}, { name: 'zs' } }
  13. // 3.对数组去重(去除重复的元素)
  14. // 之前手动去重
  15. const arr = [33, 10, 26, 30, 33, 26]
  16. const newArr = []
  17. for (const item of arr) {
  18. if (newArr.indexOf(item) !== -1) {
  19. newArr.push(item)
  20. }
  21. }
  22. // 现在利用 set 转存一下就自动去重
  23. const arrSet = new Set(arr) // 构造函数可接收可迭代对象
  24. // 去重后的数组重新存入新数组中
  25. const newArr1 = Array.from(arrSet) // 数组构造函数的 from 也能接收可迭代对象
  26. // Set 支持展开运算符,可展开存入新数组
  27. const newArr2 = [...arrSet]
  28. console.log(newArr1) // [ 33, 10, 26, 30 ]
  29. console.log(newArr2) // [ 33, 10, 26, 30 ]

Set 常见的属性:

  • size:返回Set中元素的个数;

Set 常用的方法:

  • add(value):添加某个元素,返回Set对象本身;
  • delete(value):从set中删除和这个值相等的元素,返回boolean类型;
  • has(value):判断set中是否存在某个元素,返回boolean类型;
  • clear():清空set中所有的元素,没有返回值;
  • forEach(callback, [, thisArg]):通过forEach遍历set;

另外 Set 是支持for of的遍历的。

  1. // 创建Set结构
  2. const arrSet = new Set()
  3. arrSet.add(10)
  4. arrSet.add(20)
  5. arrSet.add(30)
  6. // 1.size属性
  7. console.log(arrSet.size) // 3
  8. // 2.Set的方法
  9. // add
  10. arrSet.add(100)
  11. console.log(arrSet) // Set(4) { 10, 20, 30, 100 }
  12. // delete
  13. arrSet.delete(20)
  14. console.log(arrSet) // Set(3) { 10, 30, 100 }
  15. // has
  16. console.log(arrSet.has(100)) // true
  17. // 3.对Set进行遍历
  18. arrSet.forEach(item => {
  19. console.log(item) // 10 30 100
  20. })
  21. for (const item of arrSet) {
  22. console.log(item) // 10 30 100
  23. }
  24. // clear
  25. arrSet.clear()
  26. console.log(arrSet) // Set(0) {}

WeakSet

和 Set 类似的另外一个数据结构称之为 WeakSet,也是内部元素不能重复的数据结构。
weakSet 相比 Set 最大的不同就是它只能存放引用类型数据,并且这个引用是弱引用。

弱引用和强引用

首先我们知道 GC 回收垃圾,会从根开始判断对象是不是可达的。强引用就是可达的,而弱引用,GC 认为是不可达的。所以当一个对象只有弱引用去引用它,没有强引用引用,GC 会认为这是垃圾,需要回收。

WeakSet常见的方法:

  • add(value):添加某个元素,返回WeakSet对象本身;
  • delete(value):从WeakSet中删除和这个值相等的元素,返回boolean类型;
  • has(value):判断WeakSet中是否存在某个元素,返回boolean类型; ```javascript const weak = new WeakSet()

const arr = [1, 2, 4] let obj = {name: ‘zs’} // obj 强引用 {name: ‘zs’}

// weak.add(10) // Invalid value used in weak set weak.add(arr) weak.add(obj)

console.log(weak.has(arr)) // true console.log(weak.delete(arr)) // true console.log(weak.has(arr)); // false

// 断开强引用,{name: ‘zs’} 对象将会被 GC 回收 obj = null // 虽然 weak 集合中也指向(浅拷贝)了 {name: ‘zs’},但这是个弱引用

  1. <a name="bdKFF"></a>
  2. ## WeakSet 的使用场景
  3. WeakSet 的最大的能力就是这个弱引用,也就是我想要一个集合,里面存放对象,然后不想用这个对象了,希望回收。一般状态下,需要去断开对象其他的强引用后还要去集合中断开对对象的引用。如果这个集合是 WeakSet,集合对集合元素的引用都是弱引用,所以就不用手动去集合中断开这一步操作了。
  4. 调用对象中的方法强制只能使用该类的实例化对象来调用:
  5. ```javascript
  6. const personSet = new WeakSet()
  7. class Person {
  8. constructor() {
  9. personSet.add(this) // new实例化的时候就将this放入集合中,this就代表实例化对象
  10. }
  11. running() {
  12. // 调用方法的时候就判断下当前调用的对象是否是new 出来的对象
  13. if (!personSet.has(this)) {
  14. throw new Error("不能通过非构造方法创建出来的对象调用running方法")
  15. }
  16. console.log("running~", this)
  17. }
  18. }
  19. let p = new Person()
  20. p.running()
  21. // this放入集合,为什么要选WeakSet?
  22. p = null
  23. personSet.delete(p) // 因为当需要回收对象的时候,可以不用写这步
  24. // call 修改了this,调用running 方法的对象其实是{name: "why"},所以if判断不会通过
  25. p.running.call({name: "why"})

Map

另外一个新增的数据结构是Map,用于存储映射关系。

对象也可以存储键值对,这和 Map 有啥区别?
最大的区别就是对象的 key 只能是字符串或者 Symbol,其他数据类型做 key,也会被转成字符串。
比如对象做 key,它就会被转成[object Object]字符串。所有的对象都会被转成这同一个字符串,所以导致 value 覆盖。严格说引用类型在对象中就不能当做 key。

但是 Map 的 key,就不做要求,它是一个集合容器,允许所有数据类型成为 key。map 一般就是用来存对象。

  1. const obj1 = {}
  2. const obj2 = {}
  3. const obj = {
  4. [obj1]: 123, // []计算
  5. [obj2]: 354
  6. }
  7. console.log(obj) //{ '[object Object]': 354 }
  8. const map = new Map()
  9. map.set(obj1, 123)
  10. map.set(obj2, 456)
  11. console.log(map); // Map(2) { {} => 123, {} => 456 }

Map常见的属性:

  • size:返回Map中元素的个数;

Map常见的方法:

  • set(key, value):在Map中添加key、value,并且返回整个Map对象;
  • get(key):根据key获取Map中的value;
  • has(key):判断是否包括某一个key,返回Boolean类型;
  • delete(key):根据key删除一个键值对,返回Boolean类型;
  • clear():清空所有的元素;
  • forEach(callback, [, thisArg]):通过forEach遍历Map;

Map 也可以通过for of进行遍历,但是它遍历出来是一个数组的形式,所以结合解构方便一点。

  1. const obj1 = {}
  2. const obj2 = {}
  3. const obj = {
  4. [obj1]: 123,
  5. [obj2]: 354
  6. }
  7. console.log(obj) //{ '[object Object]': 354 }
  8. const map = new Map()
  9. // KV 添加进 map
  10. map.set(obj1, 123) // key 为对象
  11. map.set(obj2, 456)
  12. map.set('obj1', 789) // 同名,但 key 是字符串
  13. // 初始化时允许以数组的形式添加元素,注意:KV 也是数组包裹
  14. const map2 = new Map([[obj1, "aaa"], [obj2, "bbb"], [2, "ddd"]])
  15. console.log(map2)
  16. console.log(map) // Map(3) { {} => 123, {} => 456, 'obj1' => 789 }
  17. // map 长度
  18. console.log(map.size); // 3
  19. // 判断key是否在map中
  20. console.log(map.has(obj1)); // true
  21. console.log(map.has('obj1')); // true
  22. // 通过 key 获取 value
  23. console.log(map.get(obj1)); // 123
  24. console.log(map.get('obj1')); // 789
  25. // 删除键值对
  26. console.log(map.delete(obj2)); // true
  27. // 遍历 forEach
  28. map.forEach(item => { // 只有一个参数,遍历的是 value
  29. console.log(item)
  30. })
  31. map.forEach((key, value) => {
  32. console.log(key, value)
  33. })
  34. // 遍历 for of
  35. for (const item of map) {
  36. console.log(item) // item 是个数组,想要某个kv,需要索引item[0]
  37. }
  38. for (const [key, value] of map) {
  39. console.log(key, value) // {} 123, {} 456, obj1 789
  40. }
  41. // 清空map
  42. map.clear()
  43. console.log(map); //Map(0) {}

WeakMap

WeakMap 与 Map 的区别和 WeakSet、Set 相似。WeakMap 元素的 key 只能是引用类型,且为弱引用。WeakMap 也是不能遍历的,不能遍历也没有 size。

WeakMap常见的方法有四个:

  • set(key, value):在Map中添加key、value,并且返回整个Map对象;
  • get(key):根据key获取Map中的value;
  • has(key):判断是否包括某一个key,返回Boolean类型;
  • delete(key):根据key删除一个键值对,返回Boolean类型;

    WeakMap 应用场景

    应用 WeakMap 最大的原因肯定就是看中了它弱引用的特点,让对象能自动被回收。其中响应式原理中就有该应用。
    响应式的本质就是监听到变化后就执行一系列渲染函数。将被监听的对象和要绑定的函数联系起来的就是 WeakMap。 ```javascript
    {{ name }}

const obj = { name: ‘zs’, age: 18 }

  1. 比如上文,我们会先把 obj 作为 key 放入 WeakMap 中,nameage 属性作为 key 放入到 map nameage 属性渲染函数作为 value key 绑定在一起。此时再把这个包含 obj 属性和渲染函数的 map 放入 WeakMap 中作为 obj key value
  2. ```javascript
  3. const map = new Map([['name',[namefn1, namefn2]], ['age', [agefn1, agefn2]]]);
  4. const weakMap = new WeakMap([[obj, map]])
  5. obj.name = 'ls' // 监听到变化
  6. weakMap.get(obj).get('name')() // 执行对应的渲染函数