The agent feeling is a feeling of peripheral protection. I first filter the operation, and then pass it to the object
Agent basis
Create proxy
The proxy is created with a constructor and accepts two parameters, the target object and the handler object
const target = { id: 'target' } const handler = {}; const proxy = new Proxy(target,handler); console.log(target.id) console.log(proxy.id); Output: target target
Define catcher
The main purpose of an agent is to define a trap. The catcher is the 'interceptor of basic operations' defined in the handler object. Each handler object can contain zero or more traps, each corresponding to a basic operation, which can be called directly or indirectly on the proxy object. Each time these basic operations are called on the proxy object, the proxy can call the catcher function before these operations are propagated to the target object, so as to intercept and modify the corresponding behavior
That is, the catcher can block the operation, preprocess it, and then pass it to the target object
const target = { foo:'bar' } const handler = { get() { return 'handler ovveride'; } } const proxy = new Proxy(target,handler) console.log(target.foo) console.log(proxy.foo) output bar handler ovveride
Catcher parameters and reflection API s
The get() catcher will receive three parameters: the target object, the property to query, 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 output true foo true
The handwriting catcher is very troublesome, but the global (Reflect) object is paired with the proxy. All methods that can be captured and reflections have API methods with the same name
const target = { foo:'bar' } const handler = { get() { return Reflect.get(...arguments) } } const proxy = new Proxy(target,handler) console.log(proxy.foo) console.log(target.foo) perhaps const target = { foo:'bar' } const proxy = new Proxy(target, Reflect)
Revocable agent
const target = { foo: 'bar' } const handler = { get() { return 'innn' } } const = Proxy.revocable(target,handler) console.log(proxy.foo) console.log(target.foo) revokesss() console.log(proxy.foo)
Utility reflection API
Many reflection methods return a Boolean value called "tag status" to indicate whether the operation was successful, which is sometimes more useful than returning an error
Acting for another agent
Multi layer agents can be used to build a multi-layer interception network
const target = { foo: 'bar' } const firstProxy = new Proxy(target, { get() { console.log('first') return Reflect.get(...arguments) } }) const secondProxy = new Proxy(firstProxy, { get() { console.log("second") return Reflect.get(...arguments) } }) output second first bar
Agency issues
Proxy catcher and reflection method
get()
wait
That is, the operations that can be captured by the agent catcher correspond to the reflected API one by one, and the parameters also correspond to each other; At the same time, operations such as new delete, the methods above Object, and some assignment can correspond to the reflection API. Reflection can intercept their corresponding and operate
proxy pattern
Trace attribute access
const user = { name:'jake' } const proxy = new Proxy(user, { get(target, p, receiver) { console.log(`Get $`); return Reflect.get(...arguments) }, set(target, p, value, receiver) { console.log(`Set $ = $`); return Reflect.set(...arguments) } }) proxy.name; proxy.age = 22; In this way, what is done to what attribute and when are tracked at any time
Hide properties
The agent's internal code implementation is invisible to the outside, so it's easy to hide the properties on the target object
const hiddenProperties = ['foo','bar']; const targetObject = { foo: 1, bar: 2, baz: 3 } const proxy = new Proxy(targetObject,{ get(target,property) { if(hiddenProperties.includes(property)) { return undefined } else { return Reflect.get(...arguments) } }, has(target, p) { if(hiddenProperties.includes(p)) { return false; } else { return Reflect.has(...arguments) } } }) console.log(proxy.foo) console.log(proxy.bar) console.log(proxy.baz) console.log('foo' in proxy) console.log('bar' in proxy) console.log('baz' in proxy)
Attribute validation
All assignment operations will trigger the set() catcher, which can decide whether to allow or reject assignment according to the assigned value
const target = { onlyN: 0 } const proxy = new Proxy(target, { set(target, property, value, receiver) { if(typeof value !== 'number') { return false; } else }, get(target, p, receiver) { console.log('get') return Reflect.get(...arguments) } }) console.log(proxy.onlyN) proxy.onlyN = 1; console.log(proxy.onlyN)
Function and constructor parameter validation
Function and constructor parameters can be reviewed. For example, functions can only accept certain types of parameters
function median(...nums) { return nums.sort()[Math.floor(nums.length/2)] } const proxy = new Proxy(median, { apply(target, thisArg, argArray) { for(const arg of argArray) { if(typeof arg !== 'number') { throw 'NonNumber' } } return Reflect.apply(...arguments) } }) console.log(proxy(4,7,1)) console.log(proxy(4,'7',1));
Requires that arguments must be passed to the constructor
class User { constructor(id) { this.id_ = id; } } const proxy = new Proxy(User, { construct(target, argArray, newTarget) { if(argArray[0] === undefined) { throw 'user' } else { console.log('111') return Reflect.construct(...arguments) } } }) new proxy(1) new proxy()
Data binding and observable objects
Connect the parts that the runtime does not want to be associated with through the agent. For example, bind the proxy class to a global instance collection, so that all created instances are added to the collection
const userList = []; class User { constructor(name) { this.name_ = name; } } const proxy = new Proxy(User, { construct(target, argArray, newTarget) { const newUser = Reflect.construct(...arguments); userList.push(newUser) return newUser; } }) new proxy('john') new proxy('jacob') console.log(userList) output [ User { name_: 'john' }, User { name_: 'jacob' } ]