Proxy-Reflect 响应式原理

103 阅读2分钟

一、监听对象的操作

(一)利用Object.defineProperty属性描述符监听对象被设置或修改的过程

let obj = {
    name: 'why',
    age: 18
}
Object.keys(obj).forEach(key => {
    Object.defineProperty(obj, key, {
        get() {
            console.log(`监听到obj对象的${key}属性被访问了`);
        },
        set() {
            console.log(`监听到obj对象的${key}属性被设置值`);
        }
    })
})
obj.name = 'noWhy';  //监听到obj对象的name属性被设置值

//监听到obj对象的name属性被设置值
//监听到obj对象的name属性被访问了
//undefined
console.log(obj.name);

此时更改name值为noWhy,打印obj.name则为undefined,需要打印具体值应做以下操作

let obj = {
    name: 'why',
    age: 18
}
Object.keys(obj).forEach(key => {
    let value = obj[key]
    Object.defineProperty(obj, key, {
        get() {
            return value
            console.log(`监听到obj对象的${key}属性被访问了`);
        },
        set(newValue) {
            value = newValue
            console.log(`监听到obj对象的${key}属性被设置值`);
        }
    })
})
obj.name = 'noWhy';  //监听到obj对象的name属性被设置值

//监听到obj对象的name属性被设置值
//监听到obj对象的name属性被访问了
//noWhy
console.log(obj.name);

缺点: Object.defineProperty设计的初衷是为了定义对象中的属性描述符(对象属性是否可枚举、是否可写入等等),其次无法监听新增属性、删除属性

(二)Proxy

Proxy() 通过Proxy代理对象捕获代理对象所做的操作,例如属性查找、赋值、枚举、函数调用等。

const proxy = new Proxy(target, handler);
  • target: 对某个对象进行代理
  • handler: 捕获器对象,捕获对当前对象的一系列操作

1. Proxy对对象的监听

const obj = {
    name: 'why',
    age: 18
}
const objProxy = new Proxy(obj,{
    //get:监听获取对象
    //target:所代理的对象
    //key:所操作的属性的key
    get(target, key){
        console.log(target);  //{name: 'why', age: 18}
        console.log(target[key]);  //why
        return target[key];
    },

    //set:监听对象属性被修改
    //target:所代理的对象
    //key:所操作的属性的key
    //newValue:新设置的值
    set(target, key, newValue){
        console.log(newValue);  //22
        target[key] = newValue
    },

    //监听对象属性被删除
    deleteProperty(target, key){
        console.log('11111');
        delete target[key]
    },

    //监听in的捕获器
    has(target, key){
        console.log(`监听到对象的${key}属性的in操作`);
        return key in target
    }
})
let a = objProxy.name;
objProxy.age = 22
delete objProxy.name
console.log("name" in objProxy);    //in检测一个属性是否为该对象成员

2.Proxy对函数的监听

function foo(){}

const fooProxy = new Proxy(foo, {
    apply(target, thisArg, argArray){
        console.log('对foo函数进行了apply调用');
        return target.apply(thisArg, argArray)
    },
    construct(target, argArray){
        console.log('对foo函数进行了new调用');
        return new target(...argArray)
    }
})

fooProxy.apply({}, ['abc', 'cba'])
new fooProxy('abc', 'cba')

(三)Reflect

Reflect类似于Object.getPrototypeOf操作符,如浏览器支持情况下可将Object换成Reflect.getPrototypeOf

const obj = {
    name: 'why',
    age: 18
}

const objProxy = new Proxy(obj, {
    get(target, key) {
        // return target[key];
        return Reflect.get(target, key)
    },

    set(target, key, newValue) {
        // target[key] = newValue
        Reflect.set(target, key, newValue)
    },
})

objProxy.name = "noWhy"
console.log(objProxy.name);

二、响应式原理

用vue举例,当data中属性值改变,dom元素绑定的数值也会随之data改变而改变{{ value }}

  • 当obj的值发生改变时,相应的响应式函数也会触发响应
const obj = {
    name: 'why',
    age: 18
}
//封装一个响应式的函数
class Depend {
    constructor(){
        this.reactiveFns = []
    }

    addDepend(reactiveFn){
        this.reactiveFns.push(reactiveFn)
    }

    notify(){
        this.reactiveFns.forEach(fn=>{
            fn()
        })
    }
}

const depend = new Depend()
function watchFn(fn){
    depend.addDepend(fn)
}
watchFn(function (){
    console.log('11111');
})
watchFn(function (){
    console.log('22222');
})

const objProxy = new Proxy(obj, {
    get(target, key){
        return Reflect.get(target, key)
    },
    set(target, key, newValue){
        Reflect.set(target, key, newValue)
        depend.notify()
    }
})

objProxy.name = 'noWhy'