ES6 Set WeakSet Map WeakMap

2022/2/14 JavaScriptSetWeakSetMapWeakMap

# Set的基本使用

在ES6之前,我们存储数据的结构主要有两种:数组、对象。

  • 在ES6中新增了另外两种数据结构:Set、Map,以及它们的另外形式WeakSet、WeakMap。

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

  • 创建Set我们需要通过Set构造函数(暂时没有字面量创建的方式)。
//10 20 30 40 

const set = new Set()
set.add(10)
set.add(20)
set.add(30)
set.add(40)

//Set的元素不能重复
set.add(10)

//添加不同的对象
set.add({})
set.add({})


console.log(set)//Set { 10, 20, 30, 40, {}, {} }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# Set的应用场景

  • 数组去重
const arr = [33,10,26,33,26]
const arrSet = new Set(arr)
console.log(arrSet)//Set { 33, 10, 26 }
1
2
3

const arr = [33,10,26,33,26]

const newArr1 = Array.from(new Set(arr))
const newArr2 = [...new Set(arr)]

console.log(newArr1,newArr2)//[ 33, 10, 26 ]
1
2
3
4
5
6
7

# Set的常见属性与方法

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

  • add(value):添加某个元素,返回Set对象本身;

  • delete(value):从set中删除和这个值相等的元素,返回boolean类型;

  • has(value):判断set中是否存在某个元素,返回boolean类型;

  • forEach(callback,[,thisArg]):通过forEach遍历set;

  • clear():清空set中所有的元素,没有返回值;

const arr = [33,10,26,33,26]
const arrSet = new Set(arr)

console.log(arrSet.size)//3

arrSet.add(100)
console.log(arrSet)//Set { 33, 10, 26, 100 }

arrSet.delete(100)
console.log(arrSet)//Set { 33, 10, 26 }

console.log(arrSet.has(33),arrSet.has(100))//true false

arrSet.forEach((item) => {
  console.log(item)
})
//33
//10
//26

arrSet.clear()
console.log(arrSet)//Set {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

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

for( const s of arrSet){
    console.log(s)
}
//33
//10
//26
1
2
3
4
5
6

# WeakSet的使用

和 Set 类似的另外一个数据结构称之为 WeakSet ,也是内部元素不能重复的数据结构。那么和Set有什么区别呢?

  • 区别一:WeakSet 中只能存放对象类型,不能存放基本数据类型;
const weakSet = new WeakSet()
weakSet.add(10)//TypeError: Invalid value used in weak set
1
2
  • 区别二:WeakSet 对元素的引用是弱引用,如果没有其他引用对某个对象进行引用,那么 GC 可以对该对象进行回收;

# 强引用和弱引用

当指向是强引用时,有指向时,GC不会回收。而弱引用的指向会被GC回收。

image-20220214174513104

# WeakSet常见的方法:

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

# WeakSet的应用场景

注意: WeakSet不能遍历

  • 因为WeakSet只是对对象的弱引用,如果我们遍历获取到其中的元素,那么有可能造成对象不能正常的销毁。
  • 所以存储到WeakSet中的对象是没办法获取的。

当我们不想通过非构造方法创建出来的对象来调用类方法时

class Person {
  eating() {
    console.log(this, "在eating~")
  }
}

const p = new Person()

p.eating()//Person {} 在eating~
p.eating.call({ name: "okarin" }) //{ name: 'okarin' } 在eating~


const newEating = p.eating.bind("aaa")
newEating() //aaa 在eating~
1
2
3
4
5
6
7
8
9
10
11
12
13
14

使用weakSet

const personSet = new WeakSet()
class Person {
 constructor(){
     personSet.add(this)
 }
  eating() {
      if(!personSet.has(this)){
          throw new Error("不能通过非构造方法创建出来的对象调用running方法")
      }
    console.log(this, "在eating~")
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13

# Map的基本使用

另外一个新增的数据结构是Map,用于存储映射关系。 在之前我们可以使用对象来存储映射关系,他们有什么区别呢?

  • 对象存储映射关系只能用字符串(ES6新增了Symbol)作为属性名(key);
  • 某些情况下我们可能希望通过其他类型作为key,比如对象,这个时候会自动将对象转成字符串来作为key;

Map可以使用对象类型作为Key

obj1 = {
    name:"okarin"
}
obj2 = {
    name:"retr0"
}

const map = new Map()
map.set(obj1,"aaa")
map.set(obj2,"bbb")
map.set(3,"ccc")

console.log(map)

// Map {
//     { name: 'okarin' } => 'aaa',
//     { name: 'retr0' } => 'bbb',
//     3 => 'ccc'
//   }

const map2 = new Map([[obj1,"aaa"],[obj2,"bbb"],["obj3","ccc"]])
console.log(map2)


// Map {
//     { name: 'okarin' } => 'aaa',
//     { name: 'retr0' } => 'bbb',
//     'obj3' => 'ccc'
//   }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 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;

obj1 = {
    name:"okarin"
}
obj2 = {
    name:"retr0"
}
const map = new Map()

//set
map.set(obj1,"aaa")
map.set(obj2,"bbb")
map.set("obj3","ccc")
console.log(map)
// Map {
//     { name: 'okarin' } => 'aaa',
//     { name: 'retr0' } => 'bbb',
//     'obj3' => 'ccc'
//   }

//get
console.log(map.get(obj1))
//aaa

//has
console.log(map.has(obj1),map.has("obj1"))
//true false


//delete
map.delete("obj3")
console.log(map)
//Map { { name: 'okarin' } => 'aaa', { name: 'retr0' } => 'bbb' }

//forEach
map.forEach((item,key) =>{
 console.log(key,item)
})

// { name: 'okarin' } aaa
// { name: 'retr0' } bbb

//clear
map.clear()
console.log(map)
//Map {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

Map也可以通过for of进行遍历。

//遍历输出的是数组
for(const item of map){
    console.log(key,item)
}
//[ { name: 'okarin' }, 'aaa' ]
//[ { name: 'retr0' }, 'bbb' ]

//用数组下标获取值
for(const item of map){
    console.log(item[0],item[1])
}
//{ name: 'okarin' } aaa
//{ name: 'retr0' } bbb

//数组解构
for(const [key,value] of map){
    console.log(key,value)
}

//{ name: 'okarin' } aaa
//{ name: 'retr0' } bbb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# WeakMap的使用

和Map类型相似的另外一个数据结构称之为WeakMap,也是以键值对的形式存在的。那么和Map有什么区别呢?

  • 区别一:WeakMap 的 key 只能使用对象,不接受其他的类型作为key;
  • 区别二:WeakMap 的 key 对对象的引用是弱引用,如果没有其他引用引用这个对象,那么 GC 可以回收该对象;
const weakMap = new WeakMap()
obj = {
    name:"okarin"
}
weakMap.set(obj,"aaa")
1
2
3
4
5

# WeakMap常见的方法:

  • set(key,value):在Map中添加key、value,并且返回整个Map对象;
  • get(key):根据key获取Map中的value;
  • has(key):判断是否包括某一个key,返回Boolean类型;
  • delete(key):根据key删除一个键值对,返回Boolean类型;
const weakMap = new WeakMap()
obj = {
    name:"okarin"
}
weakMap.set(obj,"aaa")

console.log(weakMap.get(obj))
//aaa
console.log(weakMap.has(obj))
//true
weakMap.delete(obj)
1
2
3
4
5
6
7
8
9
10
11

# WeakMap的应用场景

Vue3的响应式原理

const obj = {
    name:"okarin",
    age:18
}

function objNameFn1(){
    console.log("objNameFn1被执行")
}

function objNameFn2(){
    console.log("objNameFn2被执行")
}

function objAgeFn1(){
    console.log("objAgeFn1被执行")
}

function objAgeFn2(){
    console.log("objAgeFn2被执行")
}

//创建weakMap
const weakMap = new WeakMap()

//收集依赖结构
//对obj收集的数据结构
const objMap = new Map()
objMap.set("name",[objNameFn1,objNameFn2])
objMap.set("age",[objAgeFn1,objAgeFn2])
weakMap.set(obj,objMap)


//如果obj.name发生了改变
obj.name = "retr0"
const targetMap = weakMap.get(obj)
const fns = targetMap.get("name")
fns.forEach(item => item())

//objNameFn1被执行
//objNameFn2被执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

当obj对象被销毁时,由于使用的是weakMap,数据结构是会被销毁的。

Last Updated: 2022/2/17下午10:28:04