Zero. Introduction
reference
- Calculation Ji Blog posts:< Detailed explanation of Proxy in JavaScript>
- Ruan Yifeng's< ECMAScript 6 getting started>- 15.Proxy
Corrected some errors and reorganized the editing and typesetting for personal learning only.
The origin of the problem
vue3.0 starts Proxy instead of object Defineproperty, causing some confusion.
- What is Proxy?
- What can Proxy do?
- Vue uses object What did defineproperty do?
- Why use Proxy instead of object defineProperty?
1, What is Proxy?
1.1 understanding Proxy
MDN definition: Proxy objects are used to define custom behaviors of basic operations (such as attribute lookup, assignment, enumeration, function call, etc.).
Generally speaking, Proxy is an object operation interceptor, which intercepts the operation of the target object and performs some custom behaviors. A layered idea is a bit similar to spring's AOP.
1.2 how to use proxy
let p = new Proxy(target, handler);
The syntax is very simple, with only two parameters, which is easy to understand
target
A target object wrapped in a Proxy (can be any type of object, including a native array, a function, or even another Proxy).
handler
An object whose properties are functions that define the behavior of an agent when an operation is performed.
var proxy = new Proxy({}, { get: function(target, property) { return 35; } }); let obj = Object.create(proxy); obj.time // 35
No action forwarding agent
The proxy object p will forward all operations applied to it to the target object target. You can directly operate on the proxy object, and the operations will be forwarded to the target object.
let target = {}; let p = new Proxy(target, {}); p.a = 37; // Forward operation to destination console.log(target.a); // 37. The operation has been forwarded correctly
1.3 operations that can be intercepted
There are 13 kinds of surrogate operations. The code (property name / method name) of each operation and the way to trigger this operation are listed below. Note that if an operation is not defined, it will be forwarded to the target object.
handler.getPrototypeOf()
This operation is triggered when reading the prototype of the proxy object, such as when executing object Getprototypeof (proxy).
handler.setPrototypeOf()
This operation is triggered when setting the prototype of the proxy object, such as when executing object Setprototypeof (proxy, null).
handler.isExtensible()
This operation is triggered when determining whether a proxy object is extensible, such as executing object Isextensible (proxy).
handler.preventExtensions()
This operation is triggered when a proxy object is made non extensible, such as object When using preventextensions (proxy).
handler.getOwnPropertyDescriptor()
This operation is triggered when the property description of a property of the proxy object is obtained, such as object Getownpropertydescriptor (proxy, "foo").
handler.defineProperty()
This operation is triggered when defining an attribute description of a proxy object, for example, when executing object When defining property (proxy, "foo", {}).
handler.has()
This operation is triggered when determining whether the proxy object has a property, such as when executing "foo" in proxy.
handler.get()
This operation is triggered when reading a property of the proxy object, such as when executing proxy Foo.
handler.set()
This operation is triggered when assigning a value to a property of the proxy object, such as when executing proxy Foo = 1.
handler.deleteProperty()
This operation is triggered when a property of the proxy object is deleted, for example, when the delete proxy Foo.
handler.ownKeys()
This operation is triggered when all the attribute keys of the proxy object are obtained, such as object Getownpropertynames (proxy).
handler.apply()
Triggered when the target object is a function and is called.
handler.construct()
This operation is triggered when constructing an instance of a proxy object whose target object is a constructor, such as when executing new proxy().
1.4 direction of this
Special attention should be paid to this pointing in proxy objects and interception operations
Once the object is proxied, his this points to the proxy object
const target = { m: function () { console.log(this === proxy); } }; const handler = {}; const proxy = new Proxy(target, handler); target.m() // false proxy.m() // true. this of the objective function points to proxy
In the interception operation defined by the handler, this points to the handler and the receiver points to the proxy
const target = { m: 100 }; const handler = { get(target, property, receiver) { console.log(this === handler) // true. this in the interception operation points to the handler console.log(receiver === proxy) // true. The receiver points to the proxy object proxy return target[property] } }; const proxy = new Proxy(target, handler); console.log(proxy.m)
2, What can Proxy do?
What can Proxy do with various commonly used interceptions? There are four commonly used operations
2.1 get()
The get method is used to intercept the read operation of a property. It can accept three parameters, namely, the target object, the property name and the proxy instance itself. All property calls will enter the same get, even if there are no properties.
- You can create properties that you don't have
- Data can be verified and converted when fetching data
- You can customize some syntax operations
- If get returns a function, it can convert a property into a method
var base = { a: 100, small: "hello world!!" }; var proxy = new Proxy(base, { get(target, property, receiver) { //Property to function if ("fn" === property) { return function(value) { console.log(value); }; } if ("ghost" === property) { return "ghost"; } if ("fn" === property) { return function(value) { console.log(value); }; } //Custom syntax sugar if (property.includes("_")) { const direct = property.split("_")[1]; const propertyBase = property.split("_")[0]; switch (direct) { case "Big": return receiver[propertyBase].toLocaleUpperCase(); default: break; } } if (!(property in target)) { throw new ReferenceError('Property "' + property + '" does not exist.'); //Validate attribute values } return target[property]; } }); console.log(proxy.a)//Output 100 normal access proxy.fn("fn")//Convert output fn attribute to method console.log(proxy.small_Big)//Output HELLO WORLD!! Custom syntax sugar console.log(proxy.ghost)//Output ghost create attribute console.log(proxy.ghost_Big)//Use of output ghost receiver console.log(proxy.b)//Error thrown during data validation
2.2 set()
The set method is used to intercept the assignment operation of an attribute. It can accept four parameters, namely, the target object, attribute name, attribute value and Proxy instance itself. The last parameter is optional.
- It can be used to verify whether the attribute meets the requirements
- Can be used to change the data format
- Can be used to listen for data change events
- Mask some assignment operations, such as "" Private variable at the beginning
var base = { a: 100, small: "hello world!!" }; const A_Change_Event = "aChangeEvent"; window.addEventListener(A_Change_Event, e => { console.log(e.data.value); }); var proxy = new Proxy(base, { set(obj, property, value, receiver) { if ("a" === property) { if (!Number.isInteger(value)) { throw new TypeError("The" + property + "is not an integer"); } if (value > 100) { throw new RangeError("The " + property + " seems invalid"); } obj[property] = value; //event const dataChangeEvent = new Event(A_Change_Event); //Create an event dataChangeEvent.data = { value }; window.dispatchEvent(dataChangeEvent); } } }); proxy.a = 80; console.log(proxy.a); proxy.a = 120;
2.3 apply()
The function object can also be the target object of the proxy
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.
The simplest example
var target = function () { return 'I am the target'; }; var handler = { apply: function () { return 'I am the proxy'; } }; var p = new Proxy(target, handler); p() // "I am the proxy"
2.4 construct ()
The new operation is intercepted by construct
Accept three parameters
- Target: target object
- args: parameter object of constructor
- newTarget: the constructor used by the new command when creating an instance object
3, Proxy and object in Vue defineProperty
3.1 Object.defineProperty implements observe
Recursively traverse all attributes, using object Defineproperty defines listening one by one
var data = {name: 'kindeng'}; observe(data); data.name = 'dmq'; // Hahaha, the listening value has changed, King -- > DMQ function observe(data) { if (!data || typeof data !== 'object') { return; } // Take out all attribute traversal Object.keys(data).forEach(function(key) { defineReactive(data, key, data[key]); }); }; function defineReactive(data, key, val) { observe(val); // Listening sub attribute Object.defineProperty(data, key, { enumerable: true, // enumerable configurable: false, // Can no longer define get: function() { return val; }, set: function(newVal) { console.log('Hahaha, the monitoring value has changed ', val, ' --> ', newVal); val = newVal; } }); }
3.2 Proxy implementation observe
There is no need to traverse the binding one by one in advance. Judge by key every time
observe(data) { const that = this; let handler = { get(target, property) { return target[property]; }, set(target, key, value) { let res = Reflect.set(target, key, value); that.subscribe[key].map(item => { item.update(); }); return res; } } this.$data = new Proxy(data, handler); }
4, Why use Proxy instead of object defineProperty?
vue3.0 uses Proxy instead of traversing object The defineproperty method adds a set to a property, which is a clumsy way to get accessors. In other words, you should not traverse, but directly monitor the data object.