ES6 Proxy和Reflect-CSDN博客

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

1.Proxy是什么

Proxy 对象用于创建一个对象的代理从而实现基本操作的拦截和自定义如属性查找、赋值、枚举、函数调用等。属于“元编程”即对编程语言进行编程。

如对对象a进行拦截就 let p = new Proxy(obj,handler)在handler里面编写拦截逻辑

又如有对象a和b想要对对象a中实现拦截对象b的属性就将对象b设置到对象a的原型上再编写拦截逻辑

2.创建Proxy对象的两种基本语法

  • 创建对象的代理
const p = new Proxy(target, handler)
  • 创建一个可撤销的代理对象
const { proxy: p, revoke } = Proxy.revocable(data, handler)

3.创建的proxy的实例有三个属性

  • target: 要使用 Proxy 包装的目标对象可以是任何类型的对象包括原生数组函数甚至另一个代理。
  • handler: 一个通常以函数作为属性的对象各属性中的函数分别定义了在执行各种操作时代理 p 的行为。(获取或者设置属性等操作handler函数触发时handler函数里面的this指向handler函数)
  • IsRevoked当前属性是否被撤销。const { proxy: p, revoke } = Proxy.revocable(data, handler)创建一个可撤销的代理对象

4.proxy的实例两个注意点

4.1 handler函数handler函数触发时handler函数里面的this指向handler函数

        let obj = { a:1 };
        let handler = {
            get(target, property, receiver){
                console.log(this);
                return target[property];//get中需要返回对应属性
            }
        };
        let p =  new Proxy(obj,handler);
        console.log(p.a);//注意要通过代理对象获取才能触发 get拦截

 

4.2 IsRevoked属性

IsRevoked当前属性是否被撤销。const { proxy: p, revoke } = Proxy.revocable(data, handler)创建一个可撤销的代理对象

通过const { proxy: p, revoke } = Proxy.revocable(data, handler)创建可被撤销的对象实例后此时IsRevoked为falsep.a 去调用属性会返回

当在调用revoke();方法用于撤销代理对象调用后IsRevoked为truep.a 去调用属性会报错

        let obj = { a:1 };
        let handler = {};
        const { proxy, revoke } = Proxy.revocable(obj, handler);
        

 5.handler中的方法handler.get()

var p = new Proxy(target, {
  get (target, property, receiver) {
  }
})

5.1方法用于拦截对象的读取属性操作。

  • target表示目标对象
  • property 被获取的属性名
  • receiver 接收Proxy或者继承Proxy对象 receiver === p
        let obj = { a: 1, b: 2 };
        let handler = {
            get(target, property, receiver) {
                console.log(target, property, receiver);
                return target[property];
            }
        };
        let proxy = new Proxy(obj, handler);
        console.log(proxy.a);

 

5.2 receiver 表示get()方法中接收的对象或者继承自的proxy对象

这里的问题是代理对象proxy.name想要获取到的是obj1上的name。

5.3handler.get()方法会拦截目标对象的以下操作:

  • 访问属性proxy[foo] 和 proxy.bar
  • 访问原型链上的属性Object.create(proxy)[foo]
  • Reflect.get()访问属性

访问属性proxy[foo] 和 proxy.bar

比如使用Proxy代理对象obj2并将proxy实例对象设置到obj1的原型上则通过obj1就可以获取到Proxy代理后的obj2的属性。此时通过proxy.name就可以获取到obj2的属性name值"lmf"

        let obj1 = { a: 1, b: 2, name: 'allen' };
        let obj2 = {
            name: 'lmf',
            get value() {
                return this.name
            }
        }
        let handler = {
            get(target, property, receiver) {
                return target[property];
                // return Reflect.get(target, property, receiver);
            }
        };
        let proxy = new Proxy(obj2, handler);
        // 将obj2的属性设置到obj1的原型上则通过obj1就可以获取到obj2的属性
        Object.setPrototypeOf(obj1, proxy);
        console.log(obj1);
        console.log(obj1.value);//lmf

 其实这里obj1.name应该为allen才对可以通过return Reflect.get(target, property, receiver);设置类似与改变this指向为目标对象target也就是obj1

        let obj1 = { a: 1, b: 2, name: 'allen' };
        let obj2 = {
            name: 'lmf',
            get value() {
                return this.name
            }
        }
        let handler = {
            get(target, property, receiver) {
                return Reflect.get(target, property, receiver);
            }
        };
        let proxy = new Proxy(obj2, handler);
        // 将obj2的属性设置到obj1的原型上则通过obj1就可以获取到obj2的属性
        Object.setPrototypeOf(obj1, proxy);
        console.log(obj1);
        console.log(obj1.value);//lmf

 访问原型链上的属性Object.create(proxy)[foo]

        // 访问原型链上的属性Object.create(proxy)[foo]
        let obj1 = { foo:'foo' };
        let handler = {
            get(target, property, receiver) {
                console.log("触发了");
                return target[property]
            }
        };
        let proxy = new Proxy(obj1, handler);
        const p = Object.create(proxy);
        console.log(p['foo']);

 Reflect.get(proxy, property)访问属性

        //  Reflect.get()访问属性
        let obj1 = { foo: 'foo' };
        let handler = {
            get(target, property, receiver) {
                console.log("触发了");
                return target[property]
            }
        };
        let proxy = new Proxy(obj1, handler);
        Reflect.get(proxy, 'foo')

6.handler中的方法handler.set()

 handler.set() 方法是设置属性值操作的捕获器。

const p = new Proxy(target, {
  set: function(target, property, value, receiver) {
  }
});

该方法会拦截目标对象的以下操作

1. 指定属性值proxy[foo] = bar 和 proxy.foo = bar

2. 指定继承者的属性值Object.create(proxy)[foo] = bar

3. Reflect.set()

        let obj1 = {
            myTime : "1697683657936"
        }
        let handler = {
            set(target, property, value){
                let setTime = value + new Date().getTime();
                target[property] = setTime;
            }
        }
        let proxy = new Proxy(obj1,handler);
        proxy.myTime = "此时的时间戳是:";
        console.log(proxy.myTime);

7.利用proxy实现的功能

7.1 验证对象的传值

需要实现的功能

1. 验证age属性的数据类型是否为整数

  2. 验证值的范围是否小于等于200

        // 需要实现的功能
        // 1. 验证age属性的数据类型是否为整数
        // 2. 验证值的范围是否小于等于200
        let user = {
            age: 100
        }

        let handler = {
            set(target,property,value){
                
                if(property==='age'){
                    if(!Number.isInteger(value)){
                        throw new TypeError("age的值必须是整数");
                    }
                    if(value>200){
                        throw new RangeError("age的值不能超过200");
                    }
                }
            }
        }

        let proxy = new Proxy(user,handler);
        // proxy.age = 22.3;
        proxy.age = 300

7.2通过属性查找数组中的特定对象

 需要实现的功能

var data = [
  { name: 'Firefox'    , type: 'browser' },
  { name: 'SeaMonkey'  , type: 'browser' },
  { name: 'Thunderbird', type: 'mailer' }
]

1. 通过索引返回对应数据 proxy[0]

2. 通过number属性返回数组长度 proxy.number

3. 通过name获取对应的数据 proxy['Firefox']

4. 通过type返回对应的数据 proxy['browser']

5. 通过types返回data中的type products.types

        var data = [
            { name: 'Firefox', type: 'browser' },
            { name: 'SeaMonkey', type: 'browser' },
            { name: 'Thunderbird', type: 'mailer' }
        ]
        let handler = {
            get(target, property) {
                // 2. 通过number属性返回数组长度 proxy.number
                if (property === 'number') {
                    return target.length;
                }

                let result = [];
                target.forEach((item, index) => {
                    // 1. 通过索引返回对应数据 proxy[0]
                    if (property == index) {
                        return result.push(item);
                    }

                    // 3. 通过name获取对应的数据 proxy['Firefox']
                    if (property === item.name) {
                        return result.push(item);
                    }

                    // 4. 通过type返回对应的数据 proxy['browser']
                    if (property === item.type) {
                        return result.push(item);
                    }
                    // 5. 通过types返回data中的type products.types
                    if (property === 'types') {
                        result.push(item.type);
                        result = [...new Set(result)]
                    }
                });
                return result;
            }
        }
        let proxy = new Proxy(data, handler);
        console.log(proxy[0]);
        console.log(proxy.number);
        console.log(proxy['Firefox']);
        console.log(proxy['browser']);
        console.log(proxy['types']);

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6