Proxy
Proxy is used to create a proxy for an object to modify the default behavior of some operations. It can be understood as setting up a layer of "interception" in front of the target object. External access to the object must first pass through this layer of interception. Therefore, it provides a mechanism for us to filter and rewrite external access. These filters can be defined by ourselves:
grammar
cosnt p = new Proxy(target, handler);
parameter
target
The target object to be wrapped with a Proxy (can be any type of object, including a native array, a function, or even another Proxy)
handler
Is an object with a function as its attribute. Its attribute is the function of the agent when performing various operations (it can be understood as the catcher of some operations of the object).
// Define a common object like Zhang San let userInfo = { name: "kobe" }; // We use proxy as a simple proxy userInfo = new Proxy(userInfo, { // Set up a catcher for reading and operating get(target, prop) { console.log('getter: Got object properties' + prop); return target[prop] }, set(target, prop, value) { console.log(`setter: Operand properties ${prop},Value is ${value}`); // You can also write some attribute setting conditions' l if(prop === 'age' && value >= 200) { throw new RangeError("I'm afraid it's not an old monster"); } // Save qualified attributes target[prop] = value; return true; } }); // Brothers, let's operate it // Read the properties of userInfo to see what happens console.log(userInfo.name); // getter: got the object attribute name Kobe // Add a new attribute to userInfo userInfo.age = 41; // setter: operation object attribute age, value 41 // Add another attribute userInfo.gender = 'male'; // setter: operand attribute gender, with value of 'male' ++userInfo.age; // getter: got the object property // setter: operation object attribute age, value 42
From the above example, we first define a user information object userInfo containing name, and then we wrap it through Proxy and return it to userInfo. At this time, userInfo becomes a Proxy instance, and all our operations on it will be intercepted by Proxy. For operations that can be set but do not set interception, they will directly fall on the target object and produce results in the original way.
Method of handler object
The following is a list of 13 interception operations supported by Proxy.
- get(target, property, receiver): Intercepts reading of object properties. For example, userinfo name.
- set(target, property, value, receiver): Intercept the setting of object properties, such as userinfo Age = 18, returns a Boolean value.
- has(target, property): Intercept the operation of prop in proxy and return a Boolean value.
- deleteProperty(target, property): Intercepts the operation of deleting object properties and returns a Boolean value.
- ownKeys(target): Intercept object getOwnPropertyNames(proxy),Object.getOwnPropertySymbols(proxy),Object.keys(proxy),for...in loop, returning an array. This method returns the property names of all its own properties of the target object, and object The returned result of keys () only includes the traversable properties of the target object itself.
- getOwnPropertyDescriptor(target, property): Intercept object Getownpropertydescriptor (proxy, propkey) returns the description object of the property.
- defineProperty(target, property, propDesc): Intercept object Defineproperty (proxy, propkey, propdesc), object. Definepropertys (proxy, propdescs), return a Boolean value.
- preventExtensions(target): Intercept object Preventextensions (proxy), which returns a Boolean value.
- getPrototypeOf(target): Intercept object Getprototypeof (proxy), return an object.
- isExtensible(target): Intercept object Isextensible (proxy), returns a Boolean value.
- setPrototypeOf(target, prototype): Intercept object Setprototypeof (proxy, proto) returns a Boolean value. If the target object is a function, there are two additional operations that can be intercepted.
- apply(target, object, args): Intercept Proxy instances as function calls, such as proxy(...args) and Proxy call(object, ...args),proxy.apply(...). You can return any value
- construct(target, args): Intercept the operation called by the Proxy instance as a constructor, such as new proxy(...args).
var userInfo = { name: "Zhang San" } const user = new Proxy(userInfo, { get(target, prop) { if (prop in target) { return target[prop]; } else { throw new ReferenceError("prop name \"" + prop + "\" does not exist."); } } }) user.name // "Zhang San" user.age // Uncaught ReferenceError: prop name "age" does not exist.
let handler = { set(target, prop, value) { if(prop === "age") { if(!Number.isInteger(value)) if(!Number.isInteger(value)) throw new TypeError('The age is not an integer'); if(value > 200) throw new RangeError('I'm afraid it's not an old monster!') } target[prop] = value; return true; } } let user = new Proxy({}, handler); user.name = "Zhang San"; // 'Zhang San' user.age = 100; // 100 user.age = 1.1; // Uncaught TypeError: The age is not an integer user.age = "old"; // Uncaught TypeError: The age is not an integer user.age = 250; // Uncaught RangeError: I'm afraid it's not an old monster!
The has() method is used to intercept the HasProperty operation, that is, this method will take effect when determining whether an object has a property. A typical operation is the in operator.
The has() method can accept two parameters: the target object and the attribute name to be queried.
The following example uses the has() method to hide some properties from the in operator.
let handler = { has(target, prop) { if(prop.indexOf('_') > -1) { return false; } return true; } } let person = { _name: "zhang", name: "Zhang San", old_name: "Zhang goudan" } let user = new Proxy(person, handler); "name" in user; // true "_name" in user; // false "old_name" in user; // false
The deleteProperty method is used to intercept the delete operation. If this method throws an error or returns false, the current property cannot be deleted by the delete command.
// Define dog egg information object let person = { name: "son of a bitch", ID_number: "88888888", age: 25, gender: 'male' } // Create proxy object let user = new Proxy(person, { deleteProperty(target, prop) { // Output information console.log(`I want to delete your ${prop}`); if(prop === 'name' || prop === 'ID_number') { throw new Error(`Second, Dad's ${prop}Can you delete it!!!`) } return true; } }) delete user.age // true delete user.name // Uncaught Error: Er Huo, can you delete dad's name!!!
The ownKeys() method is used to intercept the reading operation of the object's own properties. Specifically, intercept the following operations.
- Object.getOwnPropertyNames()
- Object.getOwnPropertySymbols()
- Object.keys()
- for...in cycle
// 1. Intercept object Examples of keys() let person = { name: "son of a bitch", ID_number: "88888888", age: 25, gender: 'male' } let user = new Proxy(person, { ownKeys(target) { // Returns a property that does not contain an underscore return Object.keys(target).filter(key => key.indexOf('_') < 0) } }) Object.keys(user); // ['name', 'age', 'gender'] // 2. Intercept object getOwnPropertyNames() Object.getOwnPropertyNames(user); // ['name', 'age', 'gender'] // 3,for...in cycle for (let key in user) { console.log(key); // name age gender } // Returns a property that you do not have let user = new Proxy(person, { ownKeys(target) { return ['name', 'age', 'a', 'b'] } }) for (let key in user) { console.log(key); // name age }
The array members returned by the ownKeys() method can only be string or Symbol values. If there are other types of values, or the returned value is not an array at all, an error will be reported
The getownpropertydescriptor () method intercepts object getOwnPropertyDescriptor(), return a property description object or undefined.
const user = new Proxy(person, { getOwnPropertyDescriptor(target, prop) { if(prop === "ID_number") return; return Object.getOwnPropertyDescriptor(target, prop); } }) Object.getOwnPropertyDescriptor(user, 'name'); // {configurable: true, enumerable: true, value: "dog egg", writable: true} Object.getOwnPropertyDescriptor(user, 'ID_number'); // undefined
The defineProperty() method intercepted object defineProperty() operation.
const user = new Proxy(person, { defineProperty (target, prop, desc) { return false; } }) user.foo = "bar"; // Will not take effect
There is no operation inside the defineProperty() method, so adding a new property is always invalid.
Note: if the target object is non extensible, defineProperty() cannot add properties that do not exist on the target object, otherwise an error will be reported. In addition, if a property of the target object is not writable or configurable, the defineProperty() method must not change these two settings.
The preventextensions() method intercepts object preventExtensions(). The method must return a Boolean value, otherwise it will be automatically converted to a Boolean value.
This method has a limitation. Only when the target object is not extensible (that is, Object.isExtensible(proxy) is false), proxy Only preventextensions can return true, otherwise an error will be reported.
var user = new Proxy(person, { preventExtensions: function(target) { return true; } }); Object.preventExtensions(user); // Uncaught TypeError: 'preventExtensions' on proxy: trap returned truish but the proxy target is extensible
In the above code, proxy The preventextensions () method returns true, but object Isextensible (proxy) will return true, so an error is reported.
In order to prevent this problem, it is usually in proxy In the preventextensions () method, call object once preventExtensions().
var user = new Proxy(person, { preventExtensions: function(target) { Object.preventExtensions(target); return true; } }); Object.preventExtensions(user); // Proxy {...}
The getPrototypeOf() method is mainly used to intercept and obtain object prototypes. Specifically, intercept the following operations.
- -Object.prototype.__proto__
- -Object.prototype.isPrototypeOf()
- -Object.getPrototypeOf()
- -Reflect.getPrototypeOf()
- -instanceof
// Define an object as common as you var obj = {}; var user = new Proxy({}, { getPrototypeOf(target) { return obj; } }) Object.getPrototypeOf(user) === obj // true
The getPrototypeOf() method intercepts object getPrototypeOf(), return obj object.
Note that the return value of getPrototypeOf() method must be object or null, otherwise an error will be reported. In addition, if the target object is not extensible, the getPrototypeOf() method must return the prototype object of the target object.
The setprototypeof () method is mainly used to intercept object setPrototypeOf() method.
var obj = {}; var target = function () {}; var handler = { setPrototypeOf (target, proto) { throw new Error('Changing the prototype is forbidden'); } }; var user = new Proxy(target, handler); Object.setPrototypeOf(user, obj); // Error: Changing the prototype is forbidden
In the above code, as long as the prototype object of target is modified, an error will be reported.
Note that this method can only return Boolean values, otherwise it will be automatically converted to Boolean values. In addition, if the target object is not extensible, the setPrototypeOf() method must not change the prototype of the target object.
The isExtensible() method intercepts object isExtensible() operation. Object. The isextensible () method is used to determine whether an object is extensible.
var user = new Proxy({}, { isExtensible: function(target) { console.log("called"); return true; } }); Object.isExtensible(user); // called true
Note that this method can only return Boolean values, otherwise the returned values will be automatically converted to Boolean values.
This method has a strong limitation. Its return value must be consistent with the isExtensible property of the target object, or an error will be thrown.
The apply method intercepts function calls, calls, and apply operations.
The apply method can accept three parameters: the target object, the context object (this) of the target object, and the parameter array of the target object.
var person = function() { return "I am a more ordinary person than Zhang San"; } var handler = { apply() { return "I was intercepted!" } } var user = new Proxy(person, handler); user(); // 'I was intercepted!' user.call(); // 'I was intercepted!' user.apply(); // 'I was intercepted!' Reflect.apply(user, null, []); // 'I was intercepted!'
In the above code, whenever the proxy function is executed (called directly or by call and apply), it will be intercepted by the apply method.
In addition, directly call reflect The apply method will also be intercepted.
The construct() method is used to intercept the new command. The following is the writing method of the intercepting object.
var user = new Proxy(function () {}, { construct: function(target, args) { console.log('called: ' + args.join(', ')); return { name: args[0]}; } }); new user('Zhang San'); // called: Zhang San // {name: 'Zhang San'}
Since the constructor is intercepted by construct(), its target object must be a function, or an error will be reported.
Note that this in the construct() method points to the handler, not the instance object.
example
The Proxy object can intercept any property of the target object, which makes it suitable for clients writing Web services.
const service = createWebService('http://exam.com/data'); service.employees().then(json => { // .... })
Create a new web service interface. Proxy can intercept any attribute of this object, so you don't need to write adaptation methods for each kind of data, just use proxy to intercept.
function createWebService(baseUrl) { return new Proxy({}, { get(target, prop) { return Http.get(`${baseUrl}/{prop}`) }, // ... }); }