携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情
前文中我们讲完了数组的响应,以及响应数组中的一些问题,今天我们来看看Map与Set。Vue2中对这两种数据结构支持并不好,我没记错的话仅仅是支持v-for时遍历。得益于vue3使用proxy去做响应式,我们终于可以实现对这两种数据结构完整的响应
Set
Set是ES6 提供的新数据结构,它类似于数组,但是成员的值不允许重复。也就是说它是一个天然的去重后的数组 它有如下几个API
- add()
- delete()
- has()
- clear()
Map
JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。
为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
如果你需要查看一下API,你可以直接在这里看Set 和 Map 数据结构,下面我直接切入正题。
首先,Map和Set和{}|[]相比,它的API是有区别的.
const obj1 = {}
obj1.a = 1
const arr = []
arr[0] = 1
const map = new Map()
map.set('a',1)
map.get('a')
const set = new Set()
set.add(1)
API上的差异就导致,我们之前的代码不能正确的代理Map与Set.
代理Set时的出现的问题
在数组中,Array.length是一个属性值,是可以直接通过[[GET]]获取,但是如果你通过Proxy去获取Set.size,那么就会被报错。因为在ECMA中,Set.size其实是一个函数而不是属性。
解决办法也比较简单,首先我们封装一个获取变量类型的函数,然后当数据是Set并且是获取size的时候,不要改变this的指向,让this指向本身,自然能够正确执行
const getType = (anything)=>Object.prototype.toString.call(anything).split('[object ')[1].replace(']','').toLowerCase()
const s = new Proxy(new Set(),{
get(target,key,receiver){
if(getType(target) === 'set'&&key === 'size'){
return Reflect.get(target,key,target)
}
/*省略其他逻辑*/
return Reflect.get(target,key,receiver)
}
})
于是,这时候我们再试一下delete,又会报错,问题还是调用delete时的this指向
const s = new Proxy(new Set(),{
get(target,key,receiver){
if(getType(target) === 'set'&&key === 'size'){
return Reflect.get(target,key,target)
}
/*省略其他逻辑*/
return target[key].bind(target)
}
})