“This is the first day of my participation in the First Challenge 2022. For details: First Challenge 2022.”
This section describes Proxy
Proxy objects are used to create a Proxy for an object to intercept and customize basic operations (such as property lookup, assignment, enumeration, function calls, and so on).
New Proxy(target, handler)
- Target: Represents the target object to intercept (this can be any type of object, including a native array, a function, or even another proxy).
- Handler: Also an object used to customize interception behavior. (Listener that listens for the behavior of the target object)
If the handler does not set up any interceptions, that is equivalent to going straight to the original object.
var target = {}; var handler = {}; var proxy = new Proxy(target, handler); proxy.a = 'b'; Target. a // "b" : Handler is an empty object and has no interception effect. Accessing proxy is equivalent to accessing targetCopy the code
The same interceptor function can be set to intercept more than one operation.
Var handler = {get: function(target, name) {if (name === 'prototype') {return Object. } return 'Hello, ' + name; }, apply: function(target, thisBinding, args) {return args[0]; }, construct: function(target, args) {return {value: args[1]}; }}; var fproxy = new Proxy(function(x, y) { return x + y; }, handler);Copy the code
Print result:
Practical Application Examples
get
The catcher for the property read operation.
get(target, property, receiver)
- Target — is the target object that is passed to the new Proxy as the first argument,
- Property — The name of the target property,
- Receiver — If the target property is a getter accessor property, receiver is the this object from which the property is read. Typically, this is the proxy object itself (or, if we inherit from a proxy, from that proxy).
Instance of a
When you try to get a nonexistent array item, you get undefined, and you wrap the regular array in a proxy to capture the read operation and return 0 if there are no properties to read.
let numbers = [0, 1, 2]; numbers = new Proxy(numbers, { get(target, prop) { if (prop in target) { return target[prop]; } else { return 0; // Default value}}}); console.log( numbers[1] ); // 1 console.log( numbers[123] ); // 0 (no such array entry)Copy the code
Example 2
Suppose you have a set of dictionaries with phrases and translations:
Let dictionary = {'Hello': 'Hello', 'Bye': 'goodbye'}; console.log( dictionary['Hello'] ); // Hola console.log( dictionary['Welcome'] ); // undefinedCopy the code
If there is no phrase we want to read, reading it from dictionary returns undefined. But in practice, returning an untranslated phrase is usually better than undefined. So in this case you can return an untranslated phrase instead of undefined.
To do this, you can wrap dictionary in a proxy that intercepts read operations:
dictionary = new Proxy(dictionary, { get(target, If (phrase in target) {return target[phrase]; if (phrase in target) {return target[phrase]; Else {// Otherwise return untranslated phrase return phrase; }}}); // Find any phrase in the dictionary! // The worst that can happen is that they are not translated. console.log( dictionary['Hello'] ); // hello console.log(dictionary['Welcome']); // Welcome (not translated)Copy the code
Examples of three
Implement access to array[-1], which accesses array elements with a negative index counting from the end. In other words, array[-n] is the same as array[array.length-n].
let array = [1, 2, 3]; array[-1] = array[2]; // 3, the last elementCopy the code
Create a proxy to implement this behavior.
let array = [1, 2, 3]; Array = new Proxy(array, {get(target, prop, receiver) {if (prop < 0) {// Even if we access it like arr[1] So we need to convert it to a number prop = +prop + target.length; } return Reflect.get(target, prop, receiver); }}); console.log(array[-1]); // 3 console.log(array[-2]); / / 2Copy the code
set
Property set the catcher for the operation.
set(target, property, value, receiver)
- Target — is the target object that is passed to the new Proxy as the first argument,
- Property — Target property name,
- Value – The value of the target attribute
- Receiver — Similar to get traps, related only to setter accessor properties.
Instance of a
This can be used for data validation, such as when the Person object has an age attribute, which should be an integer not greater than 200. Any improper assignment of the age attribute will throw an error, written like this
let validator = { set: function(obj, prop, value) { if (prop === 'age') { if (! Number.isInteger(value)) { throw new TypeError('The age is not an integer'); } if (value > 200) { throw new RangeError('The age seems invalid'); // Obj [prop] = value; obj[prop] = value; return true; }}; let person = new Proxy({}, validator);Copy the code
Print result:
has
The catcher for the IN operator.
Instance of a
This can be implemented using the HAS catcher, which uses the IN operator to check whether a number is in the range. (Because the HAS catcher intercepts the IN call.)
has(target, property)
- Target — is the target object that is passed to the new Proxy as the first argument,
- Property — The name of the property.
let range = { start: 1, end: 10 }; range = new Proxy(range, { has(target, prop) { return prop >= target.start && prop <= target.end; }}); console.log(5 in range); // true console.log(50 in range); // falseCopy the code
apply
A catcher for a function call operation.
Instance of a
The apply(Target, thisArg, args) catcher causes the proxy to be called as a function:
- Target is the target object (in JavaScript, a function is an object),
- ThisArg is the value of this.
- Args is a list of parameters.
Use Proxy instead of wrapping functions
function delay(f, ms) { return new Proxy(f, { apply(target, thisArg, args) { setTimeout(() => target.apply(thisArg, args), ms); }}); } function sayHi(user) { console.log(`Hello, ${user}! `); } sayHi = delay(sayHi, 3000); console.log(sayHi.length); // 1 (*) proxy forwards the "get Length" operation to sayHi("John"); // Hello, John! (3 seconds later)Copy the code
More attribute reference tables
Table 1:
Attribute values | Listener parameter | Listening to the content |
---|---|---|
get | (target, prop, reciver) | Listen for read properties of the target object |
set | (target, prop, value, reciver) | Listen for property assignments on the target object |
has | (target, prop) | Listen for use of the IN statement |
apply | (target, thisArg, arguments) | Listen for the calling behavior of the target function (as the target object) |
deleteProperty | (target, prop) | Listen for delete statements to delete properties of the target object |
construct | (target, arguments, newTarget) | Listen for the behavior of the target constructor (as the target object) using new to generate instances |
Table 2:
Attribute values | Listener parameter | Listening to the content |
---|---|---|
isExtensible | (target) | Listen to objext.isextensible () read |
preventExtensions | (target) | Listen for objext.preventExtensions () to read |
getOwnPropertyDescriptor | (target, prop) | Listening Objext. GetOwnPropertyDescriptor () call |
defineProperty | (target, property, descriptor) | Listen for calls to Object.defineProperty() |
ownKeys | (target) | Monitor Object. GetOwnPropertyName () to read |
getPrototypeOf | (target) | Listen for the read of objext.getProtoTypeof () |
setPrototypeOf | (target, prototype) | Listen for calls to objext.setPrototypeof () |
Resources: Proxy and Reflect
🎨 [thumbs up] [concerns] don’t get lost, more front-end dry goods waiting for you to unlock
Phase to recommend
👉 Take a look at JS prototype inheritance
Do you know how to use getters and setters in 👉 JS?
👉 In-depth understanding of ES6 arrow objects
👉 JS decorator pattern instance analysis