Open vaakian opened 2 years ago
DefineProperty
代理的是单个属性,对新增属性无能为力。
Proxy
代理的是整个对象,对新增属性依然有效。
两者都是浅层次代理,即本层代理,对象里面嵌套的对象都无效。 要实现响应式,两者都需要递归代理。
这里用两种方法实现defineReactive
,将数据进行深层递归代理。
Proxy
实现function defineReactive(obj) {
return new Proxy(obj, {
set(target, k, v) {
console.log(`set key:${k} -> ${v}`)
return target[k] = v
},
get(target, k) {
console.log(`get key: ${k}`)
let result = target[k]
// 如果是对象,递归返回一个被代理的对象
if (typeof result === 'object' && result !== null) {
result = defineReactive(result)
}
return result
}
})
}
let person = defineReactive({ name: 'xwj', family: { count: 5, list: [1, 2, 3, 4] } })
// 先得到person.family,触发get,得到一个被代理的family
// 再触发set方法
person.family.count = 66
console.log(person.family.count)
Object.defineProperty
实现2021-10-23 16:58
新增访问键链,可以知道访问从哪里开始。
function defineReactive(target, parent = []) {
let _target = {}
for (let prop of Object.keys(target)) {
// 存储键链
let keyChain = [...parent, prop]
// 劫持后的真实数据存储对象_target
_target[prop] = target[prop]
// 如果是对象,则递归劫持
if (typeof _target[prop] === 'object' && _target[prop] !== null) {
defineReactive(target[prop], keyChain)
}
// 属性不是对象,或其子属性劫持完毕,再劫持本次
// 本质上是一个前序遍历
Object.defineProperty(target, prop, {
set(newVal) {
console.log(`set: ${keyChain.join('.')} -> ${newVal}`)
return _target[prop] = newVal
},
get() {
console.log(`get: ${keyChain.join('.')}`)
return _target[prop]
}
})
}
return target
}
let obj = defineReactive({ name: 'XWJ', family: { count: 5, list: [1, 2, 3, 4] } })
obj.name = '熊维建'
console.log(obj.family.list[0])
通过以上定义的defineReactive
,能够一窥push
、shift
等函数内部到底发生了什么。
let arr = defineReactive([1, 2, 3, 4])
arr.push(123)
arr.shift()
sort干了什么?
let arr = defineReactive([1, 2, 3, 4, -5, 125, -1221])
arr.sort((a, b) => a - b)
console.log(arr)
通过输出可以看出,首先将数组复制了一份,在复制的数组上进行排序,最后统一赋值到原数组上去。
DefineProperty
defineProperty
会直接改变原对象,所以要注意避免循环触发get
和set
,即在set
中不要直接set
该对象,在get中不直接访问该对象,而是用一个变量来作为替代。 其实就是相当于原来的property
被一个内部变量完全替代。Proxy
Proxy
与defineProperty
的区别在于,他不直接侵入愿原对象,返回一个新的对象。