proxy
定义:用于修改某些操作的默认行为。
ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。
var proxy = new Proxy(target, handler);
解析:new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
let data = [1,2,3]
let p = new Proxy(data, {
get(target, key) {
console.log('获取值:', key)
return target[key]
},
set(target, key, value) {
console.log('修改值:', key, value)
target[key] = value
return true
}
})
p.push(4)
输出:
获取值: push
获取值: length
修改值: 3 4
修改值: length 4
数组和对象都可以直接触发getter和setter, 但是数组会触发两次,因为获取push和修改length的时候也会触发。
缺陷:
- 属性的新加或者删除也无法监听;
- 数组元素的增加和删除也无法监听。
Object.defineProperty
原理:该方法可以在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。
Object.defineProperty(obj, prop, descriptor) 参数:
- obj: 要在其上定义属性的对象。
- prop: 要定义或修改的属性的名称。
- descriptor: 将被定义或修改的属性的描述符。
const o = {
name:'坚持就是胜利'
}
//obj指的就是o,prop指的就是o.name
Object.defineProperty监听对象变化
需要解决的问题:
- obj对象有多个属性,可能需要循环添加Object.defineProperty里
- obj的属性可能是对象或数组,可能需要递归
- 用户可能给obj赋值新的属性,需要单独处理 解决上面问题: 定义一个对象
var obj = {
name:'坚持就是胜利',
age:12,
love:['睡觉','吃饭']
}
- 首先实现
响应式函数defineProperty
function defineProperty(obj,key,val){
observer(newVal)
Object.defineProperty(obj,key,{
get(){ // 读取方法
return val
},
set(newVal){ // 赋值监听方法
if(newVal === val)return
observer(newVal)
val = newVal
}
})
}
- 其次做个
遍历函数observer
function observer(obj){
if(typeof obj !== 'object' || obj == null){
return
}
for(const key in obj){
// 给对象中的每一个方法都设置响应式
defineProperty(obj,key,obj[key])
}
}
Object.defineProperty对象的方法式监听不到数组的变更的, 需要重写Array的原型方法
const orginalproto = Array.prototype;
const arrayProto = Object.create(orginalProto);//先克隆一份Array的原型
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
methodsToPatch.forEach(method =>{
arrayProto[method] = function(){
// 执行原始操作
orginalProto[method].apply(this,arguments)
console.log('监听赋值 成功',method)
}
})
原理就是重写数组的七个原始方法,当使用者执行这些方法时,就可以监听到数据的变化。
function observer(obj){
if(typeof obj !== 'object' || obj == null){
return
}
if(Array.isArray(obj)){
// 如果是数组,重写原型
obj._proto_ = arrayProto
// 传入的数据可能是多维度的,也需要执行响应式的
for(let i = 0;i<obj.length;i++){
observer(obj[i])
}
}else {
for(const key in obj){
// 给对象中的每一个方法都设置响应式
definePrototype(obj,key,obj[key])
}
}
}
区别
-
Object.defineProperty
- 只能监听对象(Object),不能监听数组的变化,无法触发push, pop, shift, unshift,splice, sort, reverse,必须重写数组方法。
- 必须遍历对象的每个属性
- 只能劫持当前对象属性,如果想深度劫持,必须深层遍历嵌套的对象
-
Proxy
- Proxy可以直接监听对象而非属性
- Proxy直接可以劫持整个对象,并返回一个新对象。
- Proxy可以直接监听数组的变化
- Proxy有多达13种拦截方法,不限于apply、ownKeys、deleteProperty、has等等是Object.defineProperty不具备的。