“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