# 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, {}, {} }
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 }
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 ]
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 {}
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
2
3
4
5
6
# WeakSet的使用
和 Set 类似的另外一个数据结构称之为 WeakSet ,也是内部元素不能重复的数据结构。那么和Set有什么区别呢?
- 区别一:WeakSet 中只能存放对象类型,不能存放基本数据类型;
const weakSet = new WeakSet()
weakSet.add(10)//TypeError: Invalid value used in weak set
2
- 区别二:WeakSet 对元素的引用是弱引用,如果没有其他引用对某个对象进行引用,那么 GC 可以对该对象进行回收;
# 强引用和弱引用
当指向是强引用时,有指向时,GC不会回收。而弱引用的指向会被GC回收。
# 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~
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~")
}
}
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'
// }
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 {}
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
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")
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)
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被执行
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,数据结构是会被销毁的。