Proxy、Reflect

140 阅读2分钟

先看defineProperty代理数据的缺点

const data = {
    name: 'SharkDog'
};
const { name } = data;

// 缺点显然易见(因为设计它的初衷并不是监听整个对象而是添加普通属性和描述符的而如果你想新增属性、删除属性它就显得无能为力了 这也是为什么vue2要有$set、$delete)
Object.defineProperty(data, 'name', {
    get() {
        console.log('监听到get name');

        // 不能通过data.否则就会陷入死回调(因为get方法监听指定值被获取而你这里返回时又获取了一次值就会导致每次返回每次执行又每次返回的无限漩涡)
        // return data.name;

        return name;
    },
    set(newValue) {
        console.log('监听到set name');

        this.data = newValue;
    }
});
console.log(data.name);
data.name = 'Susu';
console.log(data.name);

Proxy

// Proxy(共有13个捕获器)
const data = {
    name: 'Susu',
    age: 18
};

// Object.preventExtensions(data); 设置对象不可扩展

// 代理对象(要代理的对象, 要自定义的捕获器)
const dataProxy = new Proxy(data, {
    // 获取值捕获器(被代理的对象, 要获取值的key)
    get(target, key) {
        console.log(`Proxy代理的对象中${key}被访问了`);

        return target[key];
    },
    // 设置值捕获器(被代理的对象, 要设置的key, 新值)
    set(target, key, newValue) {
        console.log(`Proxy代理的对象中${key}被修改值为${newValue}了`);

        return target[key] = newValue;
    },
    // in捕获器
    has(target, key) {
        console.log(`Proxy代理的对象被${key} in了`);
        return key in target;
    },
    // delete捕获器
    deleteProperty(target, key) {
        console.log(`Proxy代理的对象中${key}被删除了了`);

        delete target[key];
    },
    // getPrototypeOf    获取代理对象原型__proto__
    // setPrototypeOf    设置代理对象原型
    // isExtensible      判断代理对象是否可扩展
    // preventExtensions 阻止代理对象扩展
    // getOwnPropertyDescriptor 获取代理对象属性描述符
    // defineProperty 定义代理对象属性描述符
    // ownKeys   获取代理对象所有属性描述符、获取Symbol
    // apply     代理函数被调用的捕获器
    // construct 代理对象 new调用时的捕获器
});
// 通过代理的对象获取值
console.log(dataProxy.name);

// 通过代理的对象设置值
dataProxy.age = 22;
console.log(data);
console.log(dataProxy.age);

// 通过代理的对象判断某个key是否存在
console.log('age' in dataProxy);
console.log('height' in dataProxy);

// 通过代理对象删除属性
delete dataProxy.age;
console.log(data, dataProxy);

// 代理函数
function foo() { };

const fooProxy = new Proxy(foo, {
    apply(target, thisArg, argArray) {
        console.log('调用了', target, thisArg, argArray);

        return target.apply(thisArg, argArray);
        // return target.call(thisArg, ...argArray);
    },
    construct(target, argArray, newTarget) {
        console.log('被new调用了');

        return new target(argArray)
    }
});
// 证明apply不只是apply才能触发
fooProxy();
fooProxy``;
new fooProxy;
fooProxy.call({}, 1, 2, 3);
fooProxy.apply({}, [3, 2, 1]);

Reflect

// Reflect(Proxy有哪些捕获器Reflect就有哪些方法)
const data = {
    _name: "susu",
    get name() {
        return this._name;
    },
    set name(newValue) {
        this._name = newValue
    }
};
// Proxy代理对象就是为了避免直接对对象做操作所以要用Reflect
const dataProxy = new Proxy(data, {
    // receiver是代理对象也就是(dataProxy)
    get(target, key, receiver) {
        // 既然选择了Proxy进行代理就请避免直接操作对象
        // return target[key];

        console.log(receiver, receiver === dataProxy);
        return Reflect.get(target, key, receiver);
    },
    set(target, key, newValue, receiver) {
        // Reflect是对Object的部分方法规范化
        const result = Reflect.set(target, key, newValue, receiver);
        // 且是能拿到Reflect的操作结果的(布尔值)
        if (result) console.log(key, '写入成功');
        else console.log('写入失败');
    }
});
console.log(dataProxy.name);
dataProxy.name = 'SharkDog';
console.log(dataProxy.name);