响应式框架原理2(数据劫持与代理-监听数组变化)
继续之前的记录、、
使用 Proxy 来完成代码重构:
let data = {
stage: '状态',
course: {
title: '标题',
author: ['作者1', '作者2'],
publishTime: '出版时间'
}
}
const observe = data => {
if (!data || Object.prototype.toString.call(data) !== '[object Object]') {
return
}
Object.keys(data).forEach(key => {
let currentValue = data[key]
// proxy 也可以对函数类型进行代理。这里只对承载数据类型的 object 进行处理
if (typeof currentValue === 'object') {
observe(currentValue)
data[key] = new Proxy(currentValue, {
set(target, property, value, receiver) {
// 因为数组的 push 会引起 length 属性的变化,所以 push 之后会触发两次 set 操作,只需要保留一次即可,property 为 length 时,忽略
if (property !== 'length') {
console.log(`setting ${key} value now, setting value is`, currentValue)
}
return Reflect.set(target, property, value, receiver)
}
})
} else {
Object.defineProperty(data, key, {
enumerable: true,
configurable: false,
get() {
console.log(`getting ${key} value now, getting value is:`, currentValue)
return currentValue
},
set(newValue) {
currentValue = newValue
console.log(`setting ${key} value now, setting value is`, currentValue)
}
})
}
})
}
observe(data)
对数组操作:
data.course.author.push('作者3')
上面代码在使用proxy进行代理时,并没有对getter进行代理,所以输出结果不会有getting value输出
使用 Object.defineProperty;对于键值为对象类型的情况,继续递归调用 observe 方法,并通过 Proxy 返回的新对象对 data[key] 重新赋值,这个新值的 getter 和 setter 已经被添加了代理。
结合上两篇笔记,对 Proxy 实现数据代理和 Object.defineProperty 实现数据拦截进行对比:
-
Object.defineProperty 不能监听数组的变化,需要进行数组方法的重写
-
Object.defineProperty 必须遍历对象的每个属性,且对于嵌套结构需要深层遍历
-
Proxy 的代理是针对整个对象的,而不是对象的某个属性,因此不同于 Object.defineProperty 的必须遍历对象每个属性,Proxy 只需要做一层代理就可以监听同级结构下的所有属性变化,当然对于深层结构,递归还是需要进行的
-
Proxy 支持代理数组的变化
-
Proxy 的第二个参数除了 set 和 get 以外,可以有 13 种拦截方法,比起 Object.defineProperty() 更加强大 Proxy与Reflect
-
Proxy 性能将会被底层持续优化,而 Object.defineProperty 已经不再是优化重点
网友评论