ES6的symbol与新增的数据结构---Set、WeakSet、Map、WeakMap的详解

一、Symbol的详解

1.定义

Symbol是ES6中新增的一个基本数据类型,翻译为符号

symbol的原因:

  • 在ES6之前,对象的属性名都是字符串形式,那么很容易造成属性名的冲突

  • 比如原来有一个对象,我们希望在其中添加一个新的属性和值,但是我们在不确定它原来内部有什么内容的情况下, 很容易造成冲突,从而覆盖掉它内部的某个属性

  • 比如开发中我们使用混入,那么混入中出现了同名的属性,必然有一个会被覆盖掉;

Symbol 生成一个独一无二的值:

  • Symbol值是通过Symbol函数来生成的,生成后可以作为属性名

  • 在ES6中,对象的属性名可以使用字符串,也可以使用Symbol值

Symbol即使多次创建值,它们也是不同的:Symbol函数执行后每次创建出来的值都是独一无二的;

我们也可以在创建Symbol值的时候传入一个描述description:这个是ES2019(ES10)新增的特性;

代码示例:

const s1 = Symbol()
const s2 = Symbol()

console.log(s1 === s2)

// ES2019(ES10)中, Symbol还有一个描述(description)
const s3 = Symbol("aaa")
console.log(s3.description)

2.Symbol作为属性名

通常会使用Symbol在对象中表示唯一的属性名:

应用场景:

  • 在定义对象字面量时使用

  • 新增属性

  • 通过Object.defineProperty方式定义属性

  • 获取值,注意:不能通过.(点运算符)来获取

代码示例:

// 2.Symbol作为属性名
// 2.1在定义对象字面量时使用
const obj = {
  [s1]: 'abc',
  [s2]: 'cba'
}
// 2.2新增属性
obj[s3] = 'nba'
// 2.3Object.defineProperty方式
const s4 = Symbol()
Object.defineProperty(obj, s4, {
  enumerable: true,
  configurable: true,
  writable: true,
  value: 'ccc'
})

// 注意: 不能通过.语法获取
console.log(obj[s1]); // abc

使用Symbol作为key的属性名,在遍历/Object.keys等中是获取不到这些Symbol值,需要通过需要Object.getOwnPropertySymbols来获取所有Symbol的key ,然后再遍历key来获取值

// 3.使用Symbol作为key的属性名,在遍历/Object.key等中获取不到这些Symbol值
// 需要Object.getOwnpropertySymbols来获取所有Symbol的key
console.log(Object.keys(obj)); // []
console.log(Object.getOwnPropertyNames(obj)); // []
console.log(Object.getOwnPropertySymbols(obj)); // [ Symbol(), Symbol(), Symbol(), Symbol() ]
const skeys = Object.getOwnPropertySymbols(obj)
for(const skey of skeys) {
  console.log(obj[skey]); 
}

创建相同的symbol值

// 4.Symbol.for(key)/Symbol.keyFor(symbol)
const sa = Symbol.for("aaa")
const sb = Symbol.for("aaa")

console.log(sa === sb); // true
const key = Symbol.keyFor(sa)
console.log(key); // aaa

const sc = Symbol.for(key)
console.log(sa === sc); // true

二、强引用与弱引用

引用:ES6后我们用VE来存储全局变量,他相当于根对象,里面存储了obj,所以GC垃圾回收不会把obj对象删除

let obj = {
  name: "kk"
}

强引用:当有VE有一个obj变量引用一个对象,当有另外变量foo引用这个对象(这条引用是强引用),当删除obj变量对这个对象的引用,GC并不会删除这个对象,因为foo对这个对象是强引用

弱引用:当有VE有一个obj变量引用一个对象,当有另外变量foo引用这个对象(这条引用是弱引用),当删除obj变量对这个对象的引用,GC也会删除这个对象,因为foo对这个对象是弱引用,不管它还有没有对这个对象引用,都会删除这个对象

三、Set的详解

1.定义

Set是一个新增的数据结构,可以用来保存数据类似于数组,但是和数组的区别是元素不能重复 。创建Set我们需要通过Set构造函数(暂时没有字面量创建的方式)

代码示例:

const set = new Set()
set.add(100)
set.add(90)
set.add(80)
set.add(70)
console.log(set); // Set(4) { 100, 90, 80, 70 }

// 添加对象时特别注意
// 这样是创建两个地址不同的对象
set.add({})
set.add({})

// 这是创建同一个对象
const obj = {}
set.add(obj)
set.add(obj)
console.log(set);

2.应用场景

为数组去重

// 可以为数组去重
const arr = [10, 10, 20, 30, 30, 40, 50]
const newSet = new Set(arr)
console.log(newSet);
// const newArr = Array.from(newSet)
const newArr = [...newSet]
console.log(newArr);

3.set的常见方法

  • 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的遍历的。

// Set的方法
// add
arrSet.add(100)
console.log(arrSet) // Set(5) { 33, 10, 26, 30, 100 }

// delete
arrSet.delete(33)
console.log(arrSet)

// has
console.log(arrSet.has(100))

// clear
// arrSet.clear()
console.log(arrSet)

// 6.对Set进行遍历
arrSet.forEach(item => {
  console.log(item)
})

for (const item of arrSet) {
  console.log(item)
}

四、WeakSet的详解

1.定义

和Set类似的另外一个数据结构称之为WeakSet,也是内部元素不能重复的数据结构

2.常见的方法

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

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

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

注意:WeakSet不能遍历

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

  • 所以存储到WeakSet中的对象是没办法获取的;

五、Map的详解

1.定义

Map也是新增的数据结构,用于存储映射关系

2.Map与对象的区别

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

代码示例:

// 1.JavaScript中对象中是不能使用对象来作为key的
const obj1 = { name: "kk" }
const obj2 = { name: "kobe" }

const info = {
  [obj1]: "aaa",
  [obj2]: "bbb"
}

// 先将obj1对象转为字符串 -> '[object Object]'
// obj2也转为字符串 -> '[object Object]' 第二个覆盖第一个
console.log(info) // { '[object Object]': 'bbb' }

Map的基本使用:

new Map也可以传入一个数组

// 2.Map就是允许我们对象类型来作为key的
// 构造方法的使用
const map = new Map()
map.set(obj1, "aaa")
map.set(obj2, "bbb")
map.set(1, "ccc")
console.log(map)

const map2 = new Map([[obj1, "aaa"], [obj2, "bbb"], [2, "ddd"]])
// 输出结果
/**
 *Map(3) {
    { name: 'kk' } => 'aaa',
    { name: 'kobe' } => 'bbb',
    2 => 'ddd'
  }
 */
console.log(map2)

3.常用的方法

  • 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进行遍历

// 常见的属性和方法
console.log(map2.size)

// set
map2.set("why", "eee")
console.log(map2)

// get(key)
console.log(map2.get("why"))

// has(key)
console.log(map2.has("why"))

// delete(key)
map2.delete("why")
console.log(map2)

// clear
// map2.clear()
// console.log(map2)

// 4.遍历map
map2.forEach((item, key) => {
  console.log(item, key)
})

for (const item of map2) {
  console.log(item[0], item[1])
}
// 解构的方式
for (const [key, value] of map2) {
  console.log(key, value)
}

六、WeakMap的详解

1.定义

Map类型相似的另外一个数据结构称之为WeakMap,也是以键值对的形式存在的

2.WeakMap与Map的区别

  • 区别一:WeakMap的key只能使用对象,不接受其他的类型作为key;

  • 区别二:WeakMap的key对对象想的引用是弱引用,如果没有其他引用引用这个对象,那么GC可以回收该对象;

const obj1 = {
  name: 'kk'
}
// 区别一:key只能是对象 不能是基本数据结构
const weakMap = new WeakMap()
// console.log(weakMap.set(1, "aaa")); // TypeError: Invalid value used as weak map key
console.log(weakMap.set(obj1, "aaa")); // WeakMap { <items unknown> } 因为不能使用遍历 所以这样显示

// 区别二:对对象的引用是弱引用
const map = new Map()
// 强引用
console.log(map.set(obj1, "aaa"));
// 弱引用
console.log(weakMap.set(obj1, "bbb"));

3.常见的方法

  • set(key, value):在Map中添加key、value,并且返回整个Map对象

  • get(key):根据key获取Map中的value

  • has(key):判断是否包括某一个key,返回Boolean类型

  • delete(key):根据key删除一个键值对,返回Boolean类型

  • 注意:WeakMap也是不能遍历的 ,因为没有forEach方法,也不支持通过for of的方式进行遍历

代码示例:

// get方法
console.log(weakMap.get(obj))

// has方法
console.log(weakMap.has(obj))

// delete方法
console.log(weakMap.delete(obj))
// WeakMap { <items unknown> } 因为不能遍历
console.log(weakMap)

4.应用场景

响应式原理中WeakMap的使用

收集数据依赖关系

思路:

  • 创建map对象,分别收集obj1的name和age里的函数,name作为key,函数作为value

  • 创建weakMap对象,分别收集obj1和obj2对象,obj1和obj2作为key,map作为value

代码示例:

// 应用场景 vue3响应式原理
const obj1 = {
  name: "zyk",
  age: 18
}

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

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

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

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

const obj2 = {
  name: 'hls',
  age: 18
}

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

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

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

// 2.收集依赖结构
// 2.1对obj1收集数据结构
const obj1Map = new Map()
obj1Map.set("name", [obj1NameFn1, obj1NameFn2])
obj1Map.set("age", [obj1AgeFn1, obj1AgeFn2])
console.log(obj1Map);
weakMap.set(obj1, obj1Map)
console.log(weakMap.get(obj1));

// 2.2obj2收集数据结构
const obj2Map = new Map()
obj2Map.set("name", [obj2NameFn1, obj2NameFn2])
console.log(obj2Map);
weakMap.set(obj2, obj2Map)

// 3.如果obj1.name发生改变
obj1.name = "kk"
const targetMap = weakMap.get(obj1)
console.log(targetMap);
const fns = obj1Map.get("name")
console.log(fns);
fns.forEach(item => item());

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值