ES6 Proxy and Reflect

1.What is Proxy?

The Proxy object is used to create a proxy for an object to implement interception and customization of basic operations (such as property lookup, assignment, enumeration, function calling, etc.). It belongs to “metaprogramming”, which is programming a programming language.

If you want to intercept object a, let p = new Proxy(obj,handler) and write the interception logic in the handler;

If there are objects a and b, and you want to intercept the properties of object b in object a, set object b to the prototype of object a and then write the interception logic;

2. Two basic syntaxes for creating Proxy objects

  • Create a proxy for an object
const p = new Proxy(target, handler)
  • Create a revocable proxy object
const { proxy: p, revoke } = Proxy.revocable(data, handler)

3. The created proxy instance has three attributes

  • target: The target object to be wrapped with Proxy (can be any type of object, including a native array, a function, or even another proxy).
  • handler: an object that usually has functions as attributes. The functions in each attribute define the behavior of agent p when performing various operations. (Operations such as getting or setting properties; when the handler function is triggered, this in the handler function points to the handler function)
  • IsRevoked: Whether the current attribute is revoked. const { proxy: p, revoke } = Proxy.revocable(data, handler) creates a revocable proxy object

4.Two points to note about proxy instances:

4.1 handler function: When the handler function is triggered, this in the handler function points to the handler function

 let obj = { a:1 };
        let handler = {
            get(target, property, receiver){
                console.log(this);
                return target[property];//The corresponding property needs to be returned in get
            }
        };
        let p = new Proxy(obj,handler);
        console.log(p.a);//Note that get interception must be triggered by obtaining through the proxy object

4.2 IsRevoked attribute

IsRevoked: Whether the current attribute is revoked. const { proxy: p, revoke } = Proxy.revocable(data, handler) creates a revocable proxy object

After creating an object instance that can be revoked through const { proxy: p, revoke } = Proxy.revocable(data, handler), IsRevoked is false at this time, and p.a will return when calling the attribute;

When the revoke(); method is called to revoke the proxy object, IsRevoked is true after the call, and p.a will report an error when calling the attribute;

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

5. Method handler.get() in handler

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

The

5.1 method is used to intercept the read attribute operation of the object.

  • target represents the target object
  • property The obtained property name
  • receiver receives Proxy or inherits Proxy object 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 represents the object received in the get() method or the proxy object inherited from

The problem here is that the proxy object proxy.name wants to get the name on obj1.

5.3handler.get() method will intercept the following operations of the target object:

  • Access properties: proxy[foo] and proxy.bar
  • Access properties on the prototype chain: Object.create(proxy)[foo]
  • Reflect.get() access properties

Access properties: proxy[foo] and proxy.bar

For example, if you use the Proxy object obj2 and set the proxy instance object to the prototype of obj1, you can obtain the properties of obj2 after the Proxy proxy through obj1. At this time, you can get the attribute name value “lmf” of obj2 through proxy.name

 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);
        //Set the properties of obj2 to the prototype of obj1, then you can get the properties of obj2 through obj1
        Object.setPrototypeOf(obj1, proxy);
        console.log(obj1);
        console.log(obj1.value);//lmf

In fact, obj1.name here should be allen, which can be set through return Reflect.get(target, property, receiver);, which is similar to changing this to the target object target, which is 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);
        //Set the properties of obj2 to the prototype of obj1, then you can get the properties of obj2 through obj1
        Object.setPrototypeOf(obj1, proxy);
        console.log(obj1);
        console.log(obj1.value);//lmf

Access properties on the prototype chain: Object.create(proxy)[foo]

 // Access properties on the prototype chain: Object.create(proxy)[foo]
        let obj1 = { foo:'foo' };
        let handler = {
            get(target, property, receiver) {
                console.log("Triggered");
                return target[property]
            }
        };
        let proxy = new Proxy(obj1, handler);
        const p = Object.create(proxy);
        console.log(p['foo']);

Reflect.get(proxy, property) access properties

 // Reflect.get() access properties
        let obj1 = { foo: 'foo' };
        let handler = {
            get(target, property, receiver) {
                console.log("Triggered");
                return target[property]
            }
        };
        let proxy = new Proxy(obj1, handler);
        Reflect.get(proxy, 'foo')

Method handler.set() in 6.handler

The handler.set() method is a capturer for setting property values.

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

This method will intercept the following operations of the target object

1. Specify attribute values: proxy[foo] = bar and proxy.foo = bar

2. Specify the attribute value of the inheritor: 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 = "The timestamp at this time is:";
        console.log(proxy.myTime);

7. Functions implemented using proxy

7.1 Verify the value of the object

Functions that need to be implemented

1. Verify whether the data type of the age attribute is an integer

2. Verify whether the value range is less than or equal to 200

 // Functions that need to be implemented
        // 1. Verify whether the data type of the age attribute is an integer
        // 2. Verify whether the range of values is less than or equal to 200
        let user = {
            age: 100
        }

        let handler = {
            set(target,property,value){
                
                if(property==='age'){
                    if(!Number.isInteger(value)){
                        throw new TypeError("The value of age must be an integer");
                    }
                    if(value>200){
                        throw new RangeError("The value of age cannot exceed 200");
                    }
                }
            }
        }

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

7.2 Find specific objects in an array through attributes

Functions that need to be implemented

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

1. Return the corresponding data through index proxy[0]

2. Return the array length proxy.number through the number attribute

3. Get the corresponding data through name proxy[‘Firefox’]

4. Return the corresponding data through type proxy[‘browser’]

5. Return type products.types in data through type

 var data = [
            { name: 'Firefox', type: 'browser' },
            { name: 'SeaMonkey', type: 'browser' },
            { name: 'Thunderbird', type: 'mailer' }
        ]
        let handler = {
            get(target, property) {
                // 2. Return the array length through the number attribute proxy.number
                if (property === 'number') {
                    return target.length;
                }

                let result = [];
                target.forEach((item, index) => {
                    // 1. Return the corresponding data through index proxy[0]
                    if (property == index) {
                        return result.push(item);
                    }

                    // 3. Get the corresponding data through name proxy['Firefox']
                    if (property === item.name) {
                        return result.push(item);
                    }

                    // 4. Return the corresponding data through type proxy['browser']
                    if (property === item.type) {
                        return result.push(item);
                    }
                    // 5. Return type products.types in data through type
                    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']);