Object.defineProperty、Proxy、Reflect、vue2/vue3的响应式原理

2023-11-02

Object.defineProperty()

const obj = {
        name: '小黑',
        age: 18
    }
    Object.keys(obj).forEach(key => {
        let value = obj[key]
        Object.defineProperty(obj, key, {
            get() {
                console.log(`obj.${key}被访问`);
                return value
            },
            set(newValue) {
                console.log(`obj.${key}被修改`);
                value = newValue
            },
        })
    })
    obj.name = "小白"
    console.log(obj.name);
    obj.age = 12
    console.log(obj.age);

注意:它没有办法监听更加丰富的操作,只能监听获取和修改

Proxy的基本使用

const obj = {
        name: '小黑',
        age: 18
    }
    // Proxy 参数一:需要代理的对象  参数二:捕获器  例如捕获增加,删除等
    // 可以修改原对象
    const objProxy = new Proxy(obj, {
        // target 原对象 key对象的key
        get(target, key) {
            console.log("属性被访问");
            return target[key]
        },
        // newValue 新值
        set(target, key, newValue) {
            console.log("属性被修改");
            target[key] = newValue
        }
    })
    objProxy.name = '小白'
    objProxy.age = 12
    console.log(objProxy.name);
    console.log(objProxy.age);
    // console.log(obj.name);
    // console.log(obj.age);
 

proxy的所有捕获器

  • get(target, propKey, receiver):拦截对象属性的读取,比如proxy.fooproxy['foo']

  • set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = vproxy['foo'] = v,返回一个布尔值。

  • has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。

  • deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。

  • ownKeys(target):拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。

  • getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。

  • defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一个布尔值。

  • preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。

  • getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。

  • isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。

  • setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。

  • apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)proxy.call(object, ...args)proxy.apply(...)

  • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)

Reflect

Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API。

Reflect和Proxy结合使用

    const obj = {
        name: '小黑',
        age: 18
    }
    const objProxy = new Proxy(obj, {
        get(target, key, receiver) {
            console.log("属性被访问");
            // return target[key]
            return Reflect.get(target, key)
        },
        set(target, key, newValue, receiver) {
            console.log("属性被修改");
            Reflect.set(target, key, newValue)
        }
    })
    objProxy.name = '小白'
    objProxy.age = 12
    console.log(objProxy.name);
    console.log(objProxy.age);

Proxy主要就是不希望修改原本的对象,Reflect刚好解决这个问题

Reflect的方法有返回值,可以返回是否设置成功的布尔值

Receiver的作用

const obj = {
    _name: "小黑", //    下划线是常用标记,表示是内部属性,只能通过对象的方法进行读写
    get name() {
        return this._name
    },
    set name(newValue) {
        this._name = newValue
    }
}
const objProxy = new Proxy(obj, {
    get(target, key, receiver) {
        // receiver 是代理对象
        console.log(key);
        return Reflect.get(target, key, receiver)  // receiver改变target对象get方法的this
    },
    set(target, key, newValue, receiver) {
        console.log(key);
        Reflect.set(target, key, newValue, receiver)
    }
})
objProxy.name = "小白"
console.log(objProxy.name);

Reflect.construct(target,argumentsList,newTarget)

function Student(name, age) {
    this.name = name
    this.age = age
}
function Teacher() { }
// 执行Student的内容,创建Teacher对象
const teacher = Reflect.construct(Student, ['小黑', 18], Teacher)
console.log(teacher);
console.log(teacher.__proto__ === Teacher.prototype);

Proxy响应式

// 收集依赖的响应式函数
let activeReactiveFn = null
class Depend {
    constructor() {
        // 依赖收集
        this.reactiveFns = new Set()
    }
    // addDepend(reactiveFns) {
    //     this.reactiveFns.add(reactiveFns)
    // }
​
    depend() {
        if (activeReactiveFn) {
            this.reactiveFns.add(activeReactiveFn)
        }
    }
​
    notify() {
        this.reactiveFns.forEach(fn => {
            fn()
        })
    }
}
​
// 响应式函数
const depend = new Depend()
function watchFn(fn) {
    activeReactiveFn = fn
    fn()
    activeReactiveFn = null
}
​
// 封装获取depend的函数
const targetMap = new WeakMap()
function getDepend(target, key) {
    // 根据target对象获取map的过程
    let map = targetMap.get(target)
    if (!map) {
        map = new Map()
        targetMap.set(target, map)
    }
    // 根据key获取depend对象
    let depend = map.get(key)
    if (!depend) {
        depend = new Depend()
        map.set(key, depend)
    }
    return depend
}
​
// 响应式对象的封装
function reactive(obj) {
    return new Proxy(obj, {
        get(target, key, receiver) {
            // 根据target, key获取对应的depend
            const depend = getDepend(target, key)
            // 添加函数
            depend.depend()
            return Reflect.get(target, key, receiver)  // receiver改变target对象get方法的this
        },
        set(target, key, newValue, receiver) {
            Reflect.set(target, key, newValue, receiver)
            // 根据target, key获取对应的depend
            const depend = getDepend(target, key)
            depend.notify()
        }
    })
}
​
​
// 对象的响应式
const objProxy = reactive({
    name: '小黑',
    age: 18
})
const infoProxy = reactive({
    address: '武汉',
})
watchFn(function () {
    console.log(objProxy.name, '---------------');
    console.log(objProxy.name, '+++++++++++++++');
})
watchFn(function () {
    console.log(infoProxy.address, '---------------');
    console.log(infoProxy.address, '+++++++++++++++');
})
objProxy.name = '小白'
infoProxy.address = '深圳'

Object.defineProperty响应式

// 收集依赖的响应式函数
let activeReactiveFn = null
class Depend {
    constructor() {
        // 依赖收集
        this.reactiveFns = new Set()
    }
    depend() {
        if (activeReactiveFn) {
            this.reactiveFns.add(activeReactiveFn)
        }
    }
    notify() {
        this.reactiveFns.forEach(fn => {
            fn()
        })
    }
}
​
// 响应式函数
const depend = new Depend()
function watchFn(fn) {
    activeReactiveFn = fn
    fn()
    activeReactiveFn = null
}
​
// 封装获取depend的函数
const targetMap = new WeakMap()
function getDepend(target, key) {
    // 根据target对象获取map的过程
    let map = targetMap.get(target)
    if (!map) {
        map = new Map()
        targetMap.set(target, map)
    }
    // 根据key获取depend对象
    let depend = map.get(key)
    if (!depend) {
        depend = new Depend()
        map.set(key, depend)
    }
    return depend
}
​
// 响应式对象的封装
function reactive(obj) {
    Object.keys(obj).forEach(key => {
        let value = obj[key]
        Object.defineProperty(obj,key, {
            get() {
                const depend = getDepend(obj, key)
                depend.depend()
                return value
            },
            set(newvalue) {
                value = newvalue
                const depend = getDepend(obj, key)
                depend.notify()
            }
        })
    })
    return obj
}
​
​
// 对象的响应式
const objProxy = reactive({
    name: '小黑',
    age: 18
})
const infoProxy = reactive({
    address: '武汉',
})
watchFn(function () {
    console.log(objProxy.name, '---------------');
    console.log(objProxy.name, '+++++++++++++++');
})
watchFn(function () {
    console.log(infoProxy.address, '---------------');
    console.log(infoProxy.address, '+++++++++++++++');
})
objProxy.name = '小白'
infoProxy.address = '深圳'

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Object.defineProperty、Proxy、Reflect、vue2/vue3的响应式原理 的相关文章

随机推荐