响应式中的for…in
在Vue中,我们也可以在副作用函数中使用for…in循环来枚举响应式对象的属性。
const data = {foo: 1
}
const obj = reactive(data)
effect(() => {for(const key in obj) {console.log(key)}
})
在JavaScript中,任何操作的底层实现都基于基本语义方法,for…in也不例外。在for…in中,使用了Reflect.ownKeys(obj)
来获取只属于对象自身的属性名,因此,我们可以在Proxy
中利用ownKeys()
,来对for…in的Reflect.ownKeys(obj)
进行拦截。
const bothNaN = (newValue, oldValue) => !(newValue === newValue || oldValue === oldValue)
const isEqual = (newValue, oldValue) => bothNaN(newValue, oldValue) || oldValue === newValue
function reactive(obj) {return new Proxy(obj, {get(target, key, receiver) {track(target, key)if(key === 'raw') {return target}return Reflect.get(target, key, receiver)},set(target, key, newValue, receiver) {const oldValue = target[key]if(target === receiver.raw && !isEqual(newValue, oldValue)) {target[key] = newValuetrigger(target, key)}return Reflect.set(target, key, newValue, receiver)},ownKeys(target) { // (1)track(target, ITERATE_KEY) // (2)return Reflect.ownKeys(target)}})
}
在(1)处,我们按照之前的思路,增加了拦截函数ownKeys(target)
。但在拦截收集副作用函数的时候犯了难,ownKeys
仅能拦截到target
对象,无法拦截具体的key
。因此,我们需要把key
补上,同时能够满足key
是唯一的,这里可以使用Symbol()
生成全局唯一的key
,供track
收集指定的for…in副作用函数。在(2)处,我们引入了Symbol
类型的值ITERATE_KEY
来唯一表示for…in副作用函数。
![](https://i-blog.csdnimg.cn/blog_migrate/7e2edd7cff0fdcbcb363dfbce8b15e9f.webp?x-image-process=image/format,png)
然而,当我们尝试运行修改后的代码,reactive
还是无法实现对for…in的响应式。仔细想想也能够理解,我们在收集的时候,key
是Symbol
类型的ITERATE_KEY
,而我们修改的是obj.foo
,此时触发的key
是foo
而不是ITERATE_KEY
。所以,我们需要修改触发部分的逻辑,当全局的ITERATE_KEY
存在时,ITERATE_KEY
对应的副作用函数也应该被触发。
function trigger(target, key) {const propsMap = objsMap.get(target)if(!propsMap) returnconst fns = propsMap.get(key)const iterateFns = propsMap.get