Long story JavaScript proxy base

Proxy and reflection provide developers with the ability to intercept and embed additional behavior into basic operations. Specifically, an associated proxy object can be defined for the target object, which can be used as an abstract target object. These operations can be controlled in the proxy object before the various operations on the target object affect the target object.

Create an empty proxy

Only as an abstract target object. The proxy was created using the Proxy constructor. Receive parameter target object and handler object.

const target = { 
 id: 'target' 
}; 
const handler = {}; 
const proxy = new Proxy(target, handler); 
// The id attribute accesses the same value
console.log(target.id); // target 
console.log(proxy.id); // target 
// Assigning values to target attributes is reflected on two objects
// Because both objects access the same value
target.id = 'foo'; 
console.log(target.id); // foo 
console.log(proxy.id); // foo 
// Assigning values to proxy properties is reflected on two objects
// Because this assignment is transferred to the target object
proxy.id = 'bar'; 
console.log(target.id); // bar 
console.log(proxy.id); // bar 
// The hasOwnProperty() method is in two places
// Will be applied to the target object
console.log(target.hasOwnProperty('id')); // true 
console.log(proxy.hasOwnProperty('id')); // true 
// Proxy.prototype is undefined 
// Therefore, the instanceof operator cannot be used
console.log(target instanceof Proxy); // TypeError: Function has non-object prototype 
'undefined' in instanceof check 
console.log(proxy instanceof Proxy); // TypeError: Function has non-object prototype 
'undefined' in instanceof check 
// Strict equality can be used to distinguish between proxy and target
console.log(target === proxy); // false

Define capturer

Interceptors for basic operations defined in handler objects. Each handler object can contain zero or more captures, each corresponding to a basic operation that can be called directly or indirectly on the proxy object. Each time these basic operations are invoked on a proxy object, the proxy can intercept and modify the behavior by calling the capturer function before the operations propagate to the target object.
The get() capture is triggered whenever it occurs on a proxy object.

const target = { 
 foo: 'bar' 
}; 
const handler = { 
 // Capture uses method name as key in handler object
 get() { 
 return 'handler override'; 
 } 
}; 
const proxy = new Proxy(target, handler); 
console.log(target.foo); // bar 
console.log(proxy.foo); // handler override 
console.log(target['foo']); // bar 
console.log(proxy['foo']); // handler override 
console.log(Object.create(target)['foo']); // bar 
console.log(Object.create(proxy)['foo']); // handler override

Capture Parameters and Reflection API

All capturers have access to parameters that allow them to rebuild the original behavior of the captured method.
The get() capturer receives three parameters: the target object, the attribute to be queried, and the proxy object.

const target = { 
 foo: 'bar' 
}; 
const handler = {
 get(trapTarget, property, receiver) { 
 console.log(trapTarget === target); 
 console.log(property); 
 console.log(receiver === proxy); 
 } 
}; 
const proxy = new Proxy(target, handler); 
proxy.foo; 
// true 
// foo 
// true

Instead of manually rebuilding the original behavior like get(), all captures can easily rebuild it by calling a method with the same name on the global reflect object.
All capturable methods in the handler object have corresponding Reflect API methods. These methods have the same name and function signature as the captured method and the same behavior as the intercepted method.

const target = { 
 foo: 'bar' 
};
const handler = { 
 get: Reflect.get 
}; 
const proxy = new Proxy(target, handler); 
console.log(proxy.foo); // bar 
console.log(target.foo); // bar

Simplify

const target = { 
 foo: 'bar' 
}; 
const proxy = new Proxy(target, Reflect); 
console.log(proxy.foo); // bar 
console.log(target.foo); // bar

Capture Invariant

Using a capturer can change the behavior of almost all basic methods, but it is not unlimited. Every method that does not capture a catcher knows the target object context, captures the function signature, and the behavior of the capture handler must follow the Capture Invariant. Capturer invariants vary from method to method, and they all prevent the capture definition from behaving too abnormally.
For example, the target object is not configurable and unreadable, and the capturer throws an exception when it returns a value different from that property.

const target = {}; 
Object.defineProperty(target, 'foo', { 
 configurable: false, 
 writable: false, 
 value: 'bar' 
}); 
const handler = { 
 get() { 
 return 'qux'; 
 } 
}; 
const proxy = new Proxy(target, handler); 
console.log(proxy.foo); 
// TypeError

Revokable Agent

Break the relationship between the proxy object and the target object.
The revocable() method in Proxy supports revoking the association of the proxy object with the target object, and revoking the proxy is irreversible. The revoke function (revoke()) is idempotent and results in the same number of calls, after which a TypeError is thrown when the proxy is called.

const target = { 
 foo: 'bar' 
}; 
const handler = { 
 get() { 
 return 'intercepted'; 
 } 
}; 
const { proxy, revoke } = Proxy.revocable(target, handler); 
console.log(proxy.foo); // intercepted 
console.log(target.foo); // bar 
revoke(); 
console.log(proxy.foo); // TypeError

Practical Reflection API

Reflection API s should be preferred in some cases
Reflection API and Object API
Reflection APIs are not limited to capture handlers; most reflection API methods have corresponding methods on Object types.
Status Marker
The success of an operation is called a status flag.
Provide tag status:
 Reflect.defineProperty()
 Reflect.preventExtensions()
 Reflect.setPrototypeOf()
 Reflect.set()
 Reflect.deleteProperty()

const o = {}; 
if(Reflect.defineProperty(o, 'foo', {value: 'bar'})) { 
 console.log('success'); 
} else { 
 console.log('failure'); 
}

Replace operator with first-class function
Reflect.get(): can be used instead of an object property access operator.
Reflect.set(): You can replace the = assignment operator.
Reflect.has(): can replace in operator or with(). Reflect.deleteProperty(): You can replace the delete operator.
Reflect.construct(): can replace the new operator.
Safely apply functions

Reflect.apply(myFunc, thisVal, argumentsList);

Agent another agent

A proxy can intercept operations that reflect API s, which means that it is perfectly possible to create a proxy through which to proxy another proxy.

const target = { 
 foo: 'bar' 
}; 
const firstProxy = new Proxy(target, { 
 get() { 
 console.log('first proxy'); 
 return Reflect.get(...arguments); 
 } 
}); 
const secondProxy = new Proxy(firstProxy, { 
 get() { 
 console.log('second proxy'); 
 return Reflect.get(...arguments); 
 } 
}); 
console.log(secondProxy.foo); 
// second proxy 
// first proxy 
// bar

Keywords: Javascript Front-end

Added by jaql on Mon, 27 Dec 2021 17:54:11 +0200