Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to use Proxy objects

2025-04-01 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article introduces the knowledge of "how to use Proxy objects". Many people will encounter this dilemma in the operation of actual cases, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

First, have a chat about agents

In their daily work, I believe that many friends have used Web debugging agent tools, such as Fiddler or Charles. By using the Web debugging agent tool, we can crawl HTTP/HTTPS protocol requests and manually modify the request parameters and response results. Not only that, when debugging online problems, using the Web debugging agent tool, you can also map online compressed confused JS files to local uncompressed obfuscated JS files.

After a brief introduction to the basic functions of the Web debug agent tool, let's take a look at the HTTP request flow using the Web debug agent tool:

As you can see from the figure above, after the introduction of the Web debugging agent tool, all the HTTP requests we initiated will be forwarded and processed through Web Proxy. The Web Proxy proxy layer is added to enable us to better control the flow of HTTP requests. For a single-page application, when we get the data from the server, we read the corresponding data and display it on the page:

The above process is similar to the browser getting data directly from the server:

In order to flexibly control the flow of HTTP requests, we have added a Web Proxy layer. So can we control the reading process of data objects? The answer is yes, we can use Web API, such as Object.defineProperty or Proxy API. After the introduction of Web API, the data access process is shown in the following figure:

Next, Brother Po will focus on Proxy API, which is the "hero" behind the data response of Vue3. For those of you who are interested in it, learn with Brother Po.

II. Brief introduction of Proxy object

The Proxy object is used to create a proxy for an object to intercept and customize basic operations (such as property lookups, assignments, enumerations, function calls, and so on). The constructor syntax for Proxy is:

Const p = new Proxy (target, handler)

The relevant parameters are described as follows:

Target: the target object to be wrapped in Proxy (it can be any type of object, including native arrays, functions, or even another proxy).

Handler: an object that usually takes functions as attributes, and the functions in each property define the behavior of proxy p when performing various operations.

Before we introduce an example of using a Proxy object, let's take a look at its compatibility:

(photo Source: https://caniuse.com/?search=Proxy)

As can be seen from the above picture, the compatibility of Proxy API is not very good, so we should pay attention to its compatibility when using it.

2.1 examples of using Proxy objects

After learning about the Proxy constructor, let's look at a simple example:

Const man = {name: "Po Brother",}; const proxy = new Proxy (man, {get (target, property, receiver) {console.log (`accessing ${property} attribute `); return target [property];},}); console.log (proxy.name); console.log (proxy.age)

In the above example, we used the Proxy constructor to create a proxy object for the man object. When we create the proxy object, we define a get catcher to capture the operation of property reading. The function of the catcher is to intercept the user's operations on the target object. Before these operations are propagated to the target object, the corresponding trap function will be called to intercept and modify the corresponding behavior.

After the get catcher is set, when the above sample code is run successfully, the console outputs the following result:

Accessing name property Po is accessing age property undefined

By observing the above output, we can find that the get catcher can intercept not only the read operation of known attributes, but also the read operation of unknown attributes. When creating Proxy objects, we can define other catchers, such as has, set, delete, apply, or ownKeys, in addition to defining get catchers.

2.2 traps supported by handler objects

The handler object supports 13 kinds of catchers. Here, Po lists only the following 5 commonly used catchers:

Handler.get (): the catcher for the property read operation.

Handler.set (): the catcher for the property setting operation.

Handler.deleteProperty (): the catcher of the delete operator.

Handler.ownKeys (): catcher for the Object.getOwnPropertyNames method and the Object.getOwnPropertySymbols method.

Handler.has (): the catcher of the in operator.

It is important to note that all catchers are optional. If a catcher is not defined, the default behavior of the source object is retained. After reading the introduction of the catcher above, do you think the Proxy object is very powerful?

III. Brief introduction of Reflect object

Reflect is a built-in object that provides methods to intercept JavaScript operations. These methods are the same as those of proxy handlers. Reflect is not a function object, so it is unconstructable.

Before we introduce an example of using a Reflect object, let's take a look at its compatibility:

(photo Source: https://caniuse.com/?search=Reflect)

3.1 examples of using Reflect objects

Const man = {name: "Po Brother", city: "Xiamen",}; console.log (Reflect.set (man, "sex", 1)); / / true console.log (Reflect.has (man, "name")); / / true console.log (Reflect.has (man, "age")); / / false console.log (Reflect.ownKeys (man)); / / [name', 'city',' sex']

In addition to the set, has, and ownKeys methods introduced in the example, the Reflect object also supports methods such as get, defineProperty, and deleteProperty. Below, Brother Po will briefly introduce some of the static methods supported by Reflect objects.

3.2 static methods supported by the Reflect object

All properties and methods of Reflect are static, and this object provides 13 methods related to the Proxy handler object. Similarly, Brother Po only lists the following five commonly used methods:

Reflect.get (target, propertyKey [, receiver]): gets the value of an attribute on the object, similar to target [name].

Reflect.set (target, propertyKey, value [, receiver]): a function that assigns a value to an attribute. Returns a Boolean value, or true if the update is successful.

Reflect.deleteProperty (target, propertyKey): deletes the specified property of the target object, which is equivalent to executing delete target [name].

Reflect.has (target, propertyKey): determines whether an object has an attribute, which is exactly the same as the in operator.

Reflect.ownKeys (target): returns an array of all its own attributes (excluding inherited attributes).

In actual Proxy usage scenarios, we often combine static methods provided by Reflect objects to achieve some specific functions. In order to enable you to better understand and master Proxy objects, Po will list 6 usage scenarios of Proxy objects in the next step.

4. Proxy usage scenarios

Here we first introduce the first usage scenario of Proxy objects-- enhanced arrays.

4.1 enhanced array

Define enhancedArray function

Function enhancedArray (arr) {return new Proxy (arr, {get (target, property, receiver) {const range = getRange (property); const indices = range? Range: getIndices (property); const values = indices.map (function (index) {const key = index)

< 0 ? String(target.length + index) : index; return Reflect.get(target, key, receiver); }); return values.length === 1 ? values[0] : values; }, }); function getRange(str) { var [start, end] = str.split(":").map(Number); if (typeof end === "undefined") return false; let range = []; for (let i = start; i < end; i++) { range = range.concat(i); } return range; } function getIndices(str) { return str.split(",").map(Number); } } 使用 enhancedArray 函数 const arr = enhancedArray([1, 2, 3, 4, 5]); console.log(arr[-1]); //=>

5 console.log (arr [[2,4]]); / / = > [3,5] console.log (arr [[2,-2,1]]); / / = > [3,4,2] console.log (arr ["2:4"]); / / > [3,4] console.log (arr ["- 2:3"]); / / > [4,5,1,2,3]

As can be seen from the above output, the enhanced array objects can support negative indexing, fragment indexing and other functions. In addition to enhancing arrays, we can also use Proxy API to enhance normal objects.

4.2 enhanced object

Create an enhancedObject function

Const enhancedObject = (target) = > new Proxy (target, {get (target, property) {if (property in target) {return target [property];} else {return searchFor (property, target);}},}); let value = null Function searchFor (property, target) {for (const key of Object.keys (target)) {if (typeof target [key] = "object") {searchFor (property, target [key]);} else if (typeof target [property]! = = "undefined") {value = target [property]; break;}} return value;}

Use the enhancedObject function

Const data = enhancedObject ({user: {name: "Po Brother", settings: {theme: "dark",}); console.log (data.user.settings.theme); / / dark console.log (data.theme); / / dark

After the above code runs, the console outputs the following code:

Dark dark

By observing the above output, we can see that using the objects processed by the enhancedObject function, we can easily access the deep properties inside the ordinary objects.

4.3 create a read-only object

Create a Proxy object

Const man = {name: "semlinker",}; const handler = {set: "Read-Only", defineProperty: "Read-Only", deleteProperty: "Read-Only", preventExtensions: "Read-Only", setPrototypeOf: "Read-Only",}; const proxy = new Proxy (man, handler)

Working with proxy objects

Console.log (proxy.name); proxy.name = "kakuqo"

After the above code runs, the console outputs the following code:

Semlinker proxy.name = "kakuqo"; ^ TypeError: 'Read-Only' returned for property' set' of object'#'is not a function

Looking at the above exception information, we can see that the reason for the exception is that the set property value of the handler object is not a function. If you don't want to throw a runtime exception, we can define a freeze function:

Function freeze (obj) {return new Proxy (obj, {set () {return true;}, deleteProperty () {return false;}, defineProperty () {return true;}, setPrototypeOf () {return true;}});}

After defining the freeze function, let's use an array object to test its functionality:

Let frozen = freeze ([1,2,3]); frozen [0] = 6; delete frozen [0]; frozen = Object.defineProperty (frozen, 0, {value: 66}); console.log (frozen); / / [1,2,3]

After the above code is executed successfully, the console will output [1, 2, 3], and it is obvious that the array objects processed by the freeze function have been "frozen".

4.4 intercept method calls

Define traceMethodCalls function

Function traceMethodCalls (obj) {const handler = {get (target, propKey, receiver) {const origMethod = target [propKey]; / / get the original method return function (... args) {const result = origMethod.apply (this, args); console.log (propKey + JSON.stringify (args) + "- >" + JSON.stringify (result)); return result;} },}; return new Proxy (obj, handler);}

Use the traceMethodCalls function

Const obj = {multiply (x, y) {return x * y;},}; const tracedObj = traceMethodCalls (obj); tracedObj.multiply (2jue 5); / / multiply [2mai 5]-> 10

After the above code is executed successfully, the console will output multiply [2J5]-> 10, that is, we can successfully track the process of calling methods in the obj object. In fact, in addition to being able to track method calls, we can also track access to properties in an object, as shown in the following example:

Function tracePropAccess (obj, propKeys) {const propKeySet = new Set (propKeys); return new Proxy (obj, {get (target, propKey, receiver) {if (propKeySet.has (propKey)) {console.log ("GET" + propKey);} return Reflect.get (target, propKey, receiver) }, set (target, propKey, value, receiver) {if (propKeySet.has (propKey)) {console.log ("SET" + propKey + "=" + value);} return Reflect.set (target, propKey, value, receiver);},} const man = {name: "semlinker",}; const tracedMan = tracePropAccess (man, ["name"]); console.log (tracedMan.name) / / GET name; semlinker console.log (tracedMan.age); / / undefined tracedMan.name = "kakuqo"; / / SET name=kakuqo

In the above example, we define a tracePropAccess function that takes two parameters: obj and propKeys, which represent the target to be tracked and the list of attributes to be tracked, respectively. After calling the tracePropAccess function, a proxy object is returned, and when we access the properties being tracked, the console outputs the corresponding access log.

4.5 hide attributes

Create a hideProperty function

Const hideProperty = (target, prefix = "_") = > new Proxy (target, {has: (obj, prop) = >! prop.startsWith (prefix) & & prop in obj, ownKeys: (obj) = > Reflect.ownKeys (obj). Filter ((prop) = > typeof prop! = = "string" | |! prop.startsWith (prefix)), get: (obj, prop, rec) = > (prop in rec? Obj [prop]: undefined),})

Use the hideProperty function

Const man = hideProperty ({name: "Brother Po", _ pwd: "www.semlinker.com",}); console.log (man._pwd); / / undefined console.log ('_ pwd' in man); / / false console.log (Object.keys (man)); / / ['name']

By observing the above output, we can see that with Proxy API, we have implemented the hiding of the specified prefix attribute. In addition to implementing hidden attributes, we can also implement the function of validating attribute values with Proxy API.

4.6 verify attribute values

Create a validatedUser function

Const validatedUser = (target) = > new Proxy (target, {set (target, property, value) {switch (property) {case "email": const regex = / ^ ([^ ()\ [\]\,;:\ s@ "] + (\. [^ ()\ [\]\. :\ s @ "] +) *) @ (\ [[0-9] {1 a-zA-Z 3}\. [0-9] {1 a-zA-Z 3}\. [0-9] {1 recorder 3}\. [0-9] {1 Magne3}\])) $/) If (! regex.test (value)) {console.error ("The user must have a valid email"); return false;} break; case "age": if (value

< 20 || value >

80) {console.error ("A user's age must be between 20 and 80"); return false;} break;} return Reflect.set;},})

Use the validatedUser function

Let user = {email: ", age: 0,}; user = validatedUser (user); user.email =" semlinker.com "; / / The user must have a valid email user.age = 100; / / A user's age must be between 20 and 80

After the above code is executed successfully, the console outputs the following results:

The user must have a valid email A user's age must be between 20 and 80

After introducing the usage scenario of the Proxy object, let's move on to some issues related to the Proxy object.

5. Issues related to Proxy

5.1 the pointing problem of this

Const target = {foo () {return {thisIsTarget: this = target, thisIsProxy: this = proxy,};},}; const handler = {}; const proxy = new Proxy (target, handler); console.log (target.foo ()); / {thisIsTarget: true, thisIsProxy: false} console.log (proxy.foo ()); / {thisIsTarget: false, thisIsProxy: true}

After the above code is executed successfully, the console outputs the following results:

{thisIsTarget: true, thisIsProxy: false} {thisIsTarget: false, thisIsProxy: true}

As a result of the above output, the this point in the foo method is related to the current caller. It looks simple, but if you don't pay attention to it in some scenarios, there will be problems, such as the following example:

Const _ name = new WeakMap (); class Person {constructor (name) {_ name.set (this, name);} get name () {return _ name.get (this);}}

In the above example, we use the WeakMap object to store private information about the Person object. After defining the Person class, we can use it in the following ways:

Const man = new Person ("Po Brother"); console.log (man.name); / / Po Brother const proxy = new Proxy (man, {}); console.log (proxy.name); / / undefined

For the above code, when we access the name property through the proxy object, you will find that the output is undefined. This is because when you access the name property using proxy.name, the this points to the proxy object, while the _ name WeakMap object stores the man object, so the output is undefined.

However, for the above problems, the above problems would not occur if we defined the Person class as follows:

Class Person {constructor (name) {this._name = name;} get name () {return this._name;}} const man = new Person ("Po Brother"); console.log (man.name); / / Po Brother const proxy = new Proxy (man, {}); console.log (proxy.name); / / Po Brother

In addition, if you are interested in WeakMap, you can read this article on WeakMap, which you don't know.

5.2 what is the receiver parameter of the get trap

Const p = new Proxy (target, {get: function (target, property, receiver) {/ / receiver}})

The get catcher, which is used to intercept the read attribute operation of an object, contains three parameters:

Target: target object.

Property: the name of the property that was read.

Receiver: points to the current Proxy object or an object that inherits from the current Proxy.

To better understand the description of the receiver parameter, let's give a concrete example:

Const proxy = new Proxy ({}, {get: function (target, property, receiver) {return receiver;},}); console.dir (proxy.getReceiver = proxy); / / true var inherits = Object.create (proxy); console.dir (inherits.getReceiver = = inherits); / / true

So can we change the object that receiver points to? The answer is yes. Through the get method provided by the Reflect object, we can set the value of the receiver object dynamically, as shown below:

Console.dir (Reflect.get (proxy, "getReceiver", "Po Brother")

In fact, the name receiver comes from the ECMAScript specification:

[Get] (propertyKey, Receiver) → any Return the value of the property whose key is propertyKey from this object. If any ECMAScript code must be executed to retrieve the property value, Receiver is used as the this value when evaluating the code.

[Set]   (propertyKey, value, Receiver) → Boolean Set the value of the property whose key is propertyKey to value. If any ECMAScript code must be executed to set the property value, Receiver is used as the this value when evaluating the code. Returns true if the property value was set or false if it could not be set.

The above [[Get]] and [[Set]] are called internal methods, and each object in the ECMAScript engine is associated with a set of internal methods that define their runtime behavior.

It should be noted that these internal methods are not part of the ECMAScript language. For the accessor property of the object, Receiver will be used as the value of this when the internal code is executed. Similarly, using the API provided by the Reflect object, we can also change the value of this by setting the value of the receiver parameter:

Const obj = {get foo () {return this.bar;},}; console.log (Reflect.get (obj, "foo")); / / undefined console.log (Reflect.get (obj, "foo", {bar: 2021})); / / 2021

5.3 an example of a built-in constructor for packaging

Some problems may arise when using Proxy to wrap built-in constructor instances. For example, an example of using the Proxy proxy Date constructor:

Const target = new Date (); const handler = {}; const proxy = new Proxy (target, handler); proxy.getDate (); / / Error

When the above code runs, the console outputs the following exception information:

Proxy.getDate (); ^ TypeError: this is not a Date object.

The reason for the above problem is that the internal properties of some native objects can only be obtained through the correct this, so Proxy cannot proxy the properties of these native objects. So how to solve this problem? To solve this problem, we can bind the correct this for the getDate method:

Const target = new Date (); const handler = {get (target, property, receiver) {if (property = "getDate") {return target.getDate.bind (target);} return Reflect.get (target, property, receiver);},}; const proxy = new Proxy (target, handler); console.log (proxy.getDate ())

5.4 create a revocable proxy object

The Proxy.revocable () method can be used to create a revocable proxy object, which is signed as follows:

Proxy.revocable (target, handler)

The relevant parameters are described as follows:

Target: the target object to be encapsulated in Proxy. It can be any type of object, including native arrays, functions, or even another proxy object.

Handler: an object whose properties are optional functions that define the behavior of the delegate when the corresponding operation is performed.

After calling the Proxy.revocable method, the return value is an object with the structure {"proxy": proxy, "revoke": revoke}, where:

Proxy: represents the newly generated proxy object itself, no different from a proxy object created in the normal way new Proxy (target, handler), except that it can be undone.

Revoke: undo the method so that the proxy object generated with it can be undone without any arguments when called.

After learning about the revocable method, let's give a concrete example:

Const target = {}; const handler = {}; const {proxy, revoke} = Proxy.revocable (target, handler); proxy.name = "Po Brother"; console.log (proxy.name); / / Po Brother revoke (); console.log (proxy.name); / / TypeError: Revoked

When the above code runs successfully, the console outputs the following:

Brother Po Uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked at

By observing the above results, we know that when the proxy object is undone, we have no way to perform any action on the undone proxy object.

VI. Application of Proxy in open source projects

Because the Proxy object provides powerful interception capabilities, it is used in some mature open source projects to implement responsive functions, such as vue-next and observer-util projects. For the observer-util project, Brother Po has written an article about the project from observer mode to responsive design principles, which can be read by interested partners.

For the vue-next project, responsive functionality is encapsulated in the @ vue/reactivity module, which provides us with a reactive function to create responsive objects. Let's take a brief look at the implementation of the reactive function:

/ / packages/reactivity/src/reactive.ts export function reactive (target: object) {if (target & & (target as Target) [ReactiveFlags.IS_READONLY]) {return target} return createReactiveObject (target, false, mutableHandlers, mutableCollectionHandlers)}

Inside the reactive function, you will continue to call the createReactiveObject function to create a responsive object, which is also defined in the reactive.ts file. The implementation of this function is as follows:

/ / packages/reactivity/src/reactive.ts function createReactiveObject (target: Target, isReadonly: boolean, baseHandlers: ProxyHandler, collectionHandlers: ProxyHandler) {/ / omit part of the code const proxyMap = isReadonly? ReadonlyMap: reactiveMap const existingProxy = proxyMap.get (target) if (existingProxy) {return existingProxy} const proxy = new Proxy (target, targetType = TargetType.COLLECTION? CollectionHandlers: baseHandlers) proxyMap.set (target, proxy) return proxy}

Inside the createReactiveObject function, we finally see the long-awaited Proxy object. When the target object is not an object of collection type, such as Map, Set, WeakMap, and WeakSet, baseHandlers is used when creating the Proxy object, which defines the following five traps:

Export const mutableHandlers: ProxyHandler = {get, set, deleteProperty, has, ownKeys}

The get and set captors are used to collect effect functions and trigger the execution of effect functions, respectively. All right, Brother Po is just introducing the reactive function in @ vue/reactivity. I won't introduce it in detail about how the module implements responsiveness. Brother Po will write a separate article to analyze the function of the module in detail.

That's all for "how to use Proxy objects". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report