ECMAScript6’s new proxies and reflections give developers the ability to intercept and embed additional behavior into base operations. Specifically, you can define an associated proxy object for the target object, which can be used as an abstract target function.

1. Agency basis

A proxy is an abstraction of the target object. In many ways, a proxy is like a C++ pointer in that it can be used as a proxy for a target object, but is completely independent of the target object, which can be manipulated either directly or through a proxy. But direct action bypasses the agent’s behavior.

A. Create a proxy empty proxy

The proxy is created using the Proxy constructor. There are two arguments: the target object and the handler object. Missing either argument raises TypeError. To create an empty proxy, you can pass in a simple object literal as the handler object, and all operations will reach the target object unimpeded.

Without further ado, chestnut ^~^ :

Any operation performed on a proxy object is actually applied to the target object; the only perceptible difference is that the code operates on a proxy object.

const target = { id:'kkb' }; const handler = {}; const proxy = new Proxy(target,handler); // The id attribute accesses the same value console.log(target.id); //kkb console.log(proxy.id); Id = 'kaikeba'; // KKB // Assigning the target attribute returns on both functions because both objects access the same value target.id = 'kaikeba'; console.log(target.id); //kaikeba console.log(proxy.id); //kaikeba // Assigning the proxy attribute is reflected on both objects, because the assignment is passed to the target proxy.id = 'kaikebaTwo'; console.log(target.id); //kaikebaTwo console.log(proxy.id); //kaikebaTwo //hasOwnProperty() is applied to the target object in both places; console.log(target.hasOwnProperty('id')); //true console.log(proxy.hasOwnProperty('id')); //true proxy. prototype is undefined, so instanceOf operator console.log(target instanceOf Proxy) cannot be used; //TypeError : function has non-object prototype 'undefined' in instanceOf check console.log(proxy instanceOf Proxy); //TypeError : function has non-object prototype 'undefined' in instanceOf checkCopy the code

B. Define a trap

The main purpose of using a proxy is that you can define a catcher. Each handler object can contain zero or more traps, each of which corresponds to a basic operation that can be invoked directly or indirectly on a proxy object.

A trap is a synchronous interrupt in a program flow that pauses the program flow, executes a subroutine instead, and then returns to the original program flow.

Example: You can define a get() trap that fires when an ECMAScript operation calls get() in some form

const kkb = { foo:"hello word" }; Coonst Handler = {get(){return 'handler override'; }}; const proxy = new Proxy(kkb,handler);Copy the code

This will trigger the defined GET () trap when a get() operation is performed on a proxy object. Of course, get() is not a method that ECMAScript objects can call. This method can be triggered in many ways in JavaScript code and intercepted by the GET () trap. Operations such as proxy[property], proxy.property, or Object.creat(proxy)[property] trigger the basic GET () operation to get the property. Note: The trap is triggered only if you perform these operations on a proxy object.

console.log(kkb.foo)  //hello word
console.log(proxy.foo)  //handler override
Copy the code

C. Parameters and reflection API of the catcher

The original behavior of the captured method can be reconstructed based on the corresponding parameters. In the case of get() above, the catcher receives three parameters of the target object, the property to be queried, and the proxy object.

const kkb = {
    foo:"hello  word"
};
const handler = {
   
    get(trapTarget,property,receiver){
        console.log(trapTarget===kkb);    //true
	console.log(property);             //foo
	console.log(receiver===proxy);     //true
    }
};
const proxy = new Proxy(kkb,handler);
proxy.foo;
Copy the code

With these parameters, the original method of the captured method can be reconstructed;

const kkb = {
    foo:"hello  word"
};
const handler = {   
    get(trapTarget,property,receiver){
       return   trapTarget[property]
    }
};
const proxy = new Proxy(kkb,handler);
console.log(proxy.foo);   //hello  wordconsole.log(kkb.foo);     //hello  word
Copy the code

In fact, developers can easily recreate it by using a method of the same name on the global Reflect object, which encapsulates the original behavior.

All methods that can be captured in a handler object have corresponding Reflection API methods.

const kkb = { foo:"hello word" }; const handler = { get(){ return Reflect.get(... arguments) } }; // reflect. get const handler = {get: reflect.get}; const proxy = new Proxy(kkb,handler); console.log(proxy.foo); //hello wordconsole.log(kkb.foo); //hello wordCopy the code

In fact, if you wanted to create an empty proxy that could capture all methods and then forward each method to the corresponding reflection API, you could write it as follows

const kkb = {
    foo:"hello  word"
};
const proxy = new Proxy(kkb,Reflect);
console.log(proxy.foo);  //hello  word
console.log(kkb.foo);    //hello  word
Copy the code

D. Agent another agent

Proxies can intercept the operations of the reflection API, which means it is entirely possible to create a proxy that can be used to proxy another proxy, thus building multiple layers of interceptors on a target object.

const target = { foo:"hello word" }; const proxyOne = new Proxy(target,{ get(){ console.log('proxy One') return Reflect.get(... arguments) } }); const proxyTwo = new Proxy(proxyOne ,{ get(){ console.log('proxy Two') return Reflect.get(... arguments) } }); console.log(proxyTwo.foo)//proxyTwo //proxyOne //hello wordCopy the code