Offer to come, dig friends take it! I am participating in the 2022 Spring Recruit Punch card activity. Click here for details.

Ask questions

  1. What is the difference between Object.defineProperty() and proxy?
  2. Why do VUE3 choose proxy and what are the benefits?

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).

We all know how to use Proxy

const p = new Proxy(target, handler)
Copy the code

Take a look at the internal ECMAScript 2017 (ECMA-262) Proxy

You can see that two parameters are received (target, handler)

  1. Error if target is undefined
  2. runProxyCreate(target.handler)

Here is the implementation of ProxyCreate

To exclude error handling, the core code starts at 5 and creates a new empty object p,

Set p object internal methods (except for [[call]] and [[Construct]]) to the definition specified in 9.5,

Then set p’s call and Construct methods,

[[ProxyTarget]] and [[ProxyHandler]]

Return object p

What can we intercept with them?

For most operations on objects, there is a so-called “inner method” in the JavaScript specification that describes how the lowest level of operations work. For example, [[Get]], internal methods for reading properties, [[Set]], internal methods for writing properties, and so on. These methods are only used in the specification and we cannot call them directly by method name.

The Proxy capture intercepts the invocation of these methods. They are listed in the proxy specification and in the following table.

For each internal method, there is a catcher in this table: method names that can be added to the handler parameter of the new Proxy to intercept operations:

For most operations on objects, there is a so-called “inner method” in the JavaScript specification that describes how the lowest level of operations work. For example, [[Get]], internal methods for reading properties, [[Set]], internal methods for writing properties, and so on. These methods are only used in the specification and we cannot call them directly by method name.

The Proxy capture intercepts the invocation of these methods. They are listed in the proxy specification and in the following table.

For each internal method, there is a catcher in this table: method names that can be added to the handler parameter of the new Proxy to intercept operations:

Internal methods Handler method When the trigger
[[Get]] get Reads the properties
[[Set]] set Write attributes
[[HasProperty]] has inThe operator
[[Delete]] deleteProperty deleteThe operator
[[Call]] apply A function call
[[Construct]] construct newThe operator
[[GetPrototypeOf]] getPrototypeOf Object.getPrototypeOf
[[SetPrototypeOf]] setPrototypeOf Object.setPrototypeOf
[[IsExtensible]] isExtensible Object.isExtensible
[[PreventExtensions]] preventExtensions Object.preventExtensions
[[DefineOwnProperty]] defineProperty Object.defineProperty.Object.defineProperties
[[GetOwnProperty]] getOwnPropertyDescriptor Object.getOwnPropertyDescriptor.for.. in.Object.keys/values/entries
[[OwnPropertyKeys]] ownKeys Object.getOwnPropertyNames.Object.getOwnPropertySymbols.for.. in.Object.keys/values/entries

Reflect

Reflect is a built-in object that simplifies Proxy creation.

The internal methods mentioned earlier, such as [[Get]] and [[Set]], are normative only and cannot be called directly.

The Reflect object makes it possible to call these internal methods. Its methods are minimal wrappers of internal methods.

In particular, Reflect allows us to change the operators (new, delete…) As a function (reflect.construct, reflect.deleteProperty…) Perform the call. This is an interesting feature, but there is another important point here.

For every availableProxyCapture internal methods inReflectEach has a corresponding method whose name and parameters areProxySame catcher.

So, we can use Reflect to forward the action to the original object.

We can rewrite the catcher to be shorter:

get(target, prop, receiver) {
  return Reflect.get( ... arguments);
}
Copy the code

The Reflect call is named exactly the same as the catcher and takes the same arguments. They are specially designed in this way.

So return Reflect… Provides a secure way to easily forward operations and ensure that we don’t forget anything about it.

Limitations of Proxy

1. Internal slots cannot be proxy

Many built-in objects, such as maps, sets, dates, promises, and so on, use so-called “internal slots.”

Such as:

let map = new Map();

let proxy = new Proxy(map, {});

proxy.set('test', 1); // Error
Copy the code

The solution is to bind the value of get to the target object and then return it

let map = new Map(); let proxy = new Proxy(map, { get(target, prop, receiver) { let value = Reflect.get(... arguments); return typeof value == 'function' ? value.bind(target) : value; }}); proxy.set('test', 1); alert(proxy.get('test')); // 1 (working!)Copy the code

2. Private fields are the same as above

3. peoxy ! = target

This is easy to understand, proxy objects and target objects are not === =

conclusion

A Proxy is a wrapper around an object, forwarding operations on the Proxy to the object, and optionally capturing some of them.

It can wrap any type of object, including classes and functions.

Grammar:

let proxy = new Proxy(target, {
  /* trap */
});
Copy the code

… Then, we should use proxy everywhere instead of target. The broker has no properties or methods of its own. If a trap is provided, it captures the operation, otherwise it is forwarded to the target object.

We can capture

  • Get, set, deleteProperty, etc
  • Function call (Apply catcher)
  • New operation (Construct catcher)

Reflect aims to complement Proxy. For any Proxy capture, there is a Reflect call with the same parameters. We should use them to forward the call to the target object.

The limitations of the Proxy

  1. Unable to proxy internal slots for internal objects
  2. Unable to proxy private fields
  3. The proxy object is not equal to the target object

Object.defineProperty

The object.defineProperty () method directly defines a new property on an Object, or modifies an existing property of an Object, and returns the Object.

grammar

Object.defineProperties(obj, props)
Copy the code

describe

There are two main forms of property descriptors that currently exist in objects: data (property) descriptors and access descriptors (accessor properties). A data descriptor is a property with a value that can be writable or unwritable. Access descriptors are properties described by getter and setter functions. A descriptor can only be one of these two; You can’t be both.

Attribute descriptor

  • Value – the value
  • writable– if fortrue, the value can be modified, otherwise it is only readable.
  • enumerable– if fortrue, will be listed in the loop, otherwise will not be listed.
  • configurable– if fortrue, this attribute can be deleted, and these features can be modified. Otherwise, they cannot be.

Accessor properties

  • Get — a function with no arguments that works when reading properties,
  • Set — a function with one argument, called when the property is set,
  • Enumerable — Same as a data attribute,
  • Different — the same as the data attribute.

Answer the first question

What is the difference between Object.defineProperty() and proxy?

Object.defineProperty Proxy
Adds/modifies attributes of an object, defines its description, and returns the object Proxy target object, intercepts its operation and returns the proxy object
There are data descriptors and accessor descriptors Intercept 13 kinds of operations
Only regular objects can be proxy Can proxy any object (function, array, class)
Cannot proxy internal slots for internal objects

Answer the second question

Why do VUE3 choose proxy and what are the benefits?

  • Can proxy any object including arrays and functions, objects
  • More basic semantic operations (get,set,delete…) than Object.defineProperty()
  • Instead of looping through an Object and then using Object.defineProperty(), a Proxy can Proxy all properties within an Object.
  • Object.defineproperty () can only hijack attributes of an Object (adding attributes to an Object cannot be detected by vue)