JavaScript Advanced Programming (4th edition) Reading Notes

Chapter 9 _ Agency and Reflection

I think the agent is the middleman, that is, the things you want to get from the place of the goods after the corresponding processing and then return to you. And the proxy is powerful, including a variety of APIS for you to choose from, can be perfect to generate the data you need.

ECMAScript 6’s new proxies and reflections give developers the ability to intercept and embed additional behavior into basic actions. Specifically, you can define an associated proxy object for the target object, which can be used as an abstract target object. Various operations on the target object can be controlled in the proxy object before they affect the target object.

Prior to ES6, there were no proxy-like features in ECMAScript. Because proxy is a new, foundational language capability, many translators cannot translate proxy behavior into previous ECMAScript code because proxy behavior is virtually irreplaceable. To this end, proxies and reflection are only useful on platforms that support them 100 percent. You can detect if the agent exists and provide backup code if it does not. However, this can lead to code redundancy and is therefore not recommended.

9.1 Agent Basics

As explained at the beginning of this chapter, a proxy is an abstraction of the target object. In many ways, a proxy is like a C++ pointer in that it can be used as a stand-in for the target object, but is completely independent of the target object. Target objects can be manipulated either directly or through proxies. But direct action bypasses the agent’s behavior.

9.1.1 Creating an Empty Proxy

A simple proxy is an empty proxy that does nothing but act as an abstract target object. By default, all operations performed on the proxy object are propagated to the target object without any problems. Therefore, wherever the target object can be used, the proxy object associated with it can be used in the same way.

The Proxy is created using the Proxy constructor. This constructor takes two parameters: the target object and the handler object. Missing any of these parameters raises TypeError. To create an empty proxy, you can pass a simple object literal as a handler object, allowing all operations to reach the target object unimpeded.

Any operation performed on the proxy object is actually applied to the target object. The only perceptible difference is that the code operates on proxy objects:

const target = {    id: 'target' }; 
 
const handler = {}; 
 
const proxy = new Proxy(target, handler); 
 
// The id attribute accesses the same value
console.log(target.id);  // target
console.log(proxy.id);   // target 
 
// Assigning a target attribute is reflected on both objects
// Because both objects access the same value
target.id = 'foo'; 
console.log(target.id); // foo 
console.log(proxy.id);  // foo 
 
// Assigning a proxy attribute is reflected on both objects
// Because the assignment is passed to the target object
proxy.id = 'bar'; 
console.log(target.id); // bar 
console.log(proxy.id);  // bar 
 
The hasOwnProperty() method is in two places
// both apply to the target object
console.log(target.hasOwnProperty('id')); // true
console.log(proxy.hasOwnProperty('id'));  // true 
 
/ / Proxy. The prototype is undefined
// Therefore, the instanceof operator cannot be used
console.log(target instanceof Proxy); // TypeError: Function has non-object prototype 'undefined' in instanceof check 
console.log(proxy instanceof Proxy);  // TypeError: Function has non-object prototype 'undefined' in instanceof check 
 
Strict equality can be used to distinguish agents from targets
console.log(target === proxy); // false 
Copy the code

9.1.2 Defining a trap

The main purpose of using proxies is that you can define traps. A catcher is an “interceptor for basic operations” defined in a handler object. Each handler object can contain zero or more traps, each of which corresponds to a basic operation that can be invoked directly or indirectly on a proxy object. Each time these basic operations are invoked on a proxy object, the proxy can intercept and modify the corresponding behavior by calling the catcher function before the operations are propagated to the target object.

const target = {   foo: 'bar'  }; 
 
const handler = {   
  // The catcher keys the method name in the handler object
  get() {    
    return 'handler override'; }};const proxy = new Proxy(target, handler); 
 
console.log(target.foo);                    // bar
console.log(proxy.foo);                     // handler override 
 
console.log(target['foo']);                 // bar
console.log(proxy['foo']);                  // handler override 
 
console.log(Object.create(target)['foo']);  // bar 
console.log(Object.create(proxy)['foo']);   // handler override
Copy the code

Thus, when a get() operation is performed on a proxy object, the defined GET () catcher is triggered. Of course, get() is not a method that ECMAScript objects can call. This operation can be triggered in many ways in JavaScript code and intercepted by the GET () trap. Operations such as proxy[property], proxy.property, or Object.create(proxy)[property] trigger the basic GET () operation to get the property. So all of these actions trigger the GET () trap whenever they occur on the proxy object. Note that the trap is triggered only if you perform these operations on the proxy object. Performing these operations on the target object still results in normal behavior.

9.1.3 Trap parameters and reflection APIS

All catchers have access to parameters based on which the original behavior of the captured method can be reconstructed. For example, the GET () trap receives the target object, the property to be queried, and the proxy object. With these parameters, you can reconstruct the original behavior of the captured method:

const target = {   foo: 'bar'  }; 
 
const handler = {   
  get(trapTarget, property, receiver) {   
    returntrapTarget[property]; }};const proxy = new Proxy(target, handler); 
 
console.log(proxy.foo);  // bar
console.log(target.foo); // bar 
Copy the code

All catchers can reconstruct the original operation based on their parameters, but not all catchers behave as simply as get(). Therefore, the idea of doing the same by writing code by hand is unrealistic. Instead of manually rebuilding the original behavior, developers can easily rebuild it by calling a method of the same name on the global Reflect object that encapsulates the original behavior.

All methods that can be captured in a handler object have corresponding Reflection API methods. These methods have the same name and function signature as the methods intercepted by the catcher, and they also have the same behavior as the intercepted methods. Therefore, it is also possible to define empty proxy objects using the reflection API as follows:

const target = {    foo: 'bar'  }; 
 
const handler = {  
  get() { 
    return Reflect.get(...arguments);  
  } 
}; 
 
const proxy = new Proxy(target, handler); 
 
console.log(proxy.foo);   // bar 
console.log(target.foo);  // bar 
Copy the code

In fact, if you really wanted to create an empty proxy that could catch all methods and then forward each method to the corresponding reflection API, you wouldn’t even need to define a handler object:

const target = {   foo: 'bar'  }; 
 
const proxy = new Proxy(target, Reflect); 
 
console.log(proxy.foo);   // bar
console.log(target.foo);  // bar 
Copy the code

The reflection API prepares developers with boilerplate code on which they can modify captured methods with less code. For example, the following code modifies the value returned when an attribute is accessed:

const target = {   
  foo: 'bar'.baz: 'qux' 
}; 
 
const handler = {  
  get(trapTarget, property, receiver) {   
    let decoration = ' ';    
    if (property === 'foo') {  
      decoration = '!!!!!! ';    
    } 
 
    return Reflect.get(...arguments) + decoration;  
  }
}; 
 
const proxy = new Proxy(target, handler); 
 
console.log(proxy.foo);   // bar!!!
console.log(target.foo);  // bar 
 
console.log(proxy.baz);   // qux 
console.log(target.baz);  // qux 
Copy the code

9.1.4 Capture invariant

Using a catcher can change the behavior of almost any basic method, but it is not without limitations. According to the ECMAScript specification, each capturing method knows the target object context, capturing function signatures, and capturing handlers must behave according to “trap invariant.” The catcher invariant varies from method to method, but generally prevents the catcher definition from behaving too perversely.

For example, if the target object has a non-configurable and writable data attribute, TypeError is raised when the catcher returns a value different from that attribute:

const target = {}; 
Object.defineProperty(target, 'foo',
  {   
    configurable: false.writable: false.value: 'bar'});const handler = {  
  get() {     
    return 'qux'; }};const proxy = new Proxy(target, handler); 
 
console.log(proxy.foo); // TypeError 
Copy the code

9.1.5 Revocable agent

Sometimes it may be necessary to break the connection between the proxy object and the target object. For ordinary proxies created using new Proxy(), this connection persists for the lifetime of the Proxy object.

Proxy also exposes the Revocable () method, which supports unassociating Proxy objects with target objects. Revoking an agent is irreversible. Furthermore, the revoke function () is idempotent, yielding the same result no matter how many times it is called. Calling the proxy after it has been revoked raises TypeError. The undo function and the proxy object are generated simultaneously at instantiation time:

const target = {    foo: 'bar' }; 
 
const handler = {   
  get() {    
    return 'intercepted'; }};const { proxy, revoke } = Proxy.revocable(target, handler); 
 
console.log(proxy.foo);   // intercepted 
console.log(target.foo);  // bar 
 
revoke(); 
 
console.log(proxy.foo);   // TypeError 
Copy the code

9.1.6 Practical reflection API

1. Reflection API vs. object API

When using the reflection API, remember:

  1. Reflection apis are not limited to capture handlers;
  2. Most reflection API methods have corresponding methods on the Object type.

2. Status flag

Many reflection methods return a Boolean value called a “status flag” that indicates whether the intended operation was successful. Sometimes, status flags are more useful than reflection API methods that return modified objects or throw errors (depending on the method).

// Initial code
 
const o = {}; 
 
try {   
  Object.defineProperty(o, 'foo'.'bar'); 
  console.log('success');
} catch(e) {  
  console.log('failure');
} 
Copy the code

Reflect.defineproperty () returns false instead of throwing an error if a problem occurs while defining a new property. So using this reflection method you can refactor the above code as follows:

// Refactored code
 
const o = {}; 
 
if(Reflect.defineProperty(o, 'foo', {value: 'bar'{}))console.log('success');
} else { 
  console.log('failure'); 
} 
Copy the code

The following reflection methods all provide status flags:

  • Reflect.defineProperty()
  • Reflect. PreventExtensions () to the original object not extend, and return the original object.
  • Reflect.setPrototypeOf()
  • Reflect.set()
  • Reflect.deleteProperty()

3. Replace operators with first-class functions

The following reflection methods provide operations that can only be done with an operator.

  • Reflect.get() : Can replace the object property access operator.
  • Reflect.set() : Can replace the = assignment operator.
  • Reflect.has() : Can replace the in operator or with().
  • Reflect.deleteproperty () : Can replace the delete operator.
  • Reflect.construct() : Can replace the new operator.

4. Safely apply functions

When a function is called through the Apply method, it is possible (though unlikely) that the called function also defines its own Apply attribute. To get around this problem, use the Apply method defined on the Function prototype. This horrible code could just as well use reflect.apply:

Function.prototype.apply.call(myFunc, thisVal, argumentList); 
/ / Reflect left
Reflect.apply(myFunc, thisVal, argumentsList); 
Copy the code

9.1.7 Agent Another agent

The proxy can intercept the operations of the reflection API, which means it is entirely possible to create a proxy through which to proxy another. This allows you to build multiple layers of interceptors on top of a target object.

const target = {    foo: 'bar' }; 
 
const firstProxy = new Proxy(target, 
  {  
    get() {   
      console.log('first proxy');   
      return Reflect.get(... arguments); }});const secondProxy = new Proxy(firstProxy,
  {   
    get() {    
      console.log('second proxy');  
      return Reflect.get(... arguments); }});console.log(secondProxy.foo); 
// second proxy 
// first proxy 
// bar 
Copy the code

9.1.8 Problems and deficiencies of agency

1. This in the proxy

We know that this in a method usually refers to the object on which the method was called:

const target = {   
  thisValEqualsProxy() { 
    return this=== proxy; }}const proxy = new Proxy(target, {}); 
 
console.log(target.thisValEqualsProxy());  // false 
console.log(proxy.thisValEqualsProxy());   // true 
Copy the code

However, if the target object relies on object identification, you may encounter unexpected problems.

2. Agents and internal slots

Proxies and instances of built-in reference types (such as Array) usually work well together, but some ECMAScript built-in types can rely on mechanisms beyond the control of the proxy, resulting in errors in calling certain methods on the proxy.

A classic example is the Date type. According to the ECMAScript specification, the execution of Date type methods depends on the internal slot [[NumberDate]] on this value. The inner slot does not exist on the proxy object, and the value of the inner slot is not accessible through normal get() and set() operations, so methods that should have been forwarded to the target object after the proxy intercepts raise TypeError:

const target = new Date(a);const proxy = new Proxy(target, {}); 
 
console.log(proxy instanceof Date);  // true 
 
proxy.getDate();  // TypeError: 'this' is not a Date object 
Copy the code

9.2 Proxy capture and reflection methods

The agent can capture 13 different basic operations. These operations have different reflection API methods, parameters, associated ECMAScript operations, and invariants.

9.2.1 the get ()

The GET () trap is called during the operation to get the property value. The corresponding reflection API method is reflect.get ().

const myTarget = {};  
 
const proxy = new Proxy(myTarget, 
  {  
    get(target, property, receiver) {   
      console.log('get()');  
      return Reflect.get(... arguments) } } ); proxy.foo;// get()
Copy the code

1. The return value

The return value is unlimited.

2. Intercept operations

  • proxy.property
  • proxy[property]
  • Object.create(proxy)[property]
  • Reflect.get(proxy, property, receiver)

3. Trap handler parameters

  • Target: indicates the target object.
  • Property: String key property on the referenced target object.
  • Receiver: Proxy object or object that inherits proxy object.

4. Capture invariant

If the target.property is not writable and configurable, the value returned by the handler must match the target.property. If target.property is not configurable and the [[Get]] property is undefined, the return value of the handler must also be undefined.

9.2.2 set ()

The set() trap is called during an operation to set the property value. The corresponding reflection API method is reflect.set ().

const myTarget = {};  
 
const proxy = new Proxy(myTarget,
  {  
    set(target, property, value, receiver) {  
      console.log('set()');    
      return Reflect.set(... arguments) } } ); proxy.foo ='bar'; // set() 
Copy the code

1. The return value

Return true to indicate success; Returning false indicates failure, and TypeError is raised in strict mode.

2. Intercept operations

  • proxy.property = value
  • proxy[property] = value
  • Object.create(proxy)[property] = value
  • Reflect.get(proxy, property, receiver)

3. Trap handler parameters

  • Target: indicates the target object.
  • Property: String key property on the referenced target object.
  • Value: The value to be assigned to the attribute.
  • Receiver: The object that receives the initial value.

4. Capture invariant

If target.property is not writable and configurable, the value of the target property cannot be modified. If target.property is not configurable and the [[Set]] property is undefined, the value of the target property cannot be modified. In strict mode, returning false in the handler raises TypeError.

9.2.3 has ()

The HAS () trap is called in the IN operator. The corresponding reflection API method is reflect.has ().

const myTarget = {};  
 
const proxy = new Proxy(myTarget, 
  {  
    has(target, property) {    
      console.log('has()');     
      return Reflect.has(... arguments) } } );'foo' in proxy; // has() 
Copy the code

1. The return value

Has () must return a Boolean value indicating whether the attribute exists. Returns non-Boolean values are converted to booleans.

2. Intercept operations

  • property in proxy
  • property in Object.create(proxy)
  • with(proxy) {(property); }
  • Reflect.has(proxy, property)

3. Trap handler parameters

  • Target: indicates the target object.
  • Property: String key property on the referenced target object.

4. Capture invariant

If target.property exists and cannot be configured, the handler must return true. If target.property exists and the target object is not extensible, the handler must return true.

9.2.4 defineProperty ()

DefineProperty () traps are called in Object.defineProperty(). The corresponding reflection API method is reflect.defineProperty ().

const myTarget = {};  
 
const proxy = new Proxy(myTarget, 
  {   
    defineProperty(target, property, descriptor) {
      console.log('defineProperty()'); 
      return Reflect.defineProperty(... arguments) } } );Object.defineProperty(proxy, 'foo', { value: 'bar' }); // defineProperty() 
Copy the code

1. The return value

DefineProperty () must return a Boolean value indicating whether the attribute was successfully defined. Returns non-Boolean values are converted to booleans.

2. Intercept operations

  • Object.defineProperty(proxy, property, descriptor)
  • Reflect.defineProperty(proxy, property, descriptor)

3. Trap handler parameters

  • Target: indicates the target object.
  • Property: String key property on the referenced target object.
  • Descriptor: Contains optional enumerable, 64x, writable, value, get, and set definitions.

4. Capture invariant

Attributes cannot be defined if the target object is not extensible. If the target object has a configurable property, you cannot add a non-configurable property of the same name. If the target object has a non-configurable property, you cannot add a configurable property of the same name.

9.2.5 getOwnPropertyDescriptor ()

GetOwnPropertyDescriptor () will capture the Object. GetOwnPropertyDescriptor () is invoked. The corresponding reflection API method to Reflect getOwnPropertyDescriptor ().

const myTarget = {};  
 
const proxy = new Proxy(myTarget, 
  {  
    getOwnPropertyDescriptor(target, property) {   
      console.log('getOwnPropertyDescriptor()');     
      return Reflect.getOwnPropertyDescriptor(... arguments) } } );Object.getOwnPropertyDescriptor(proxy, 'foo'); // getOwnPropertyDescriptor() 
Copy the code

1. The return value

GetOwnPropertyDescriptor () must return the object, or undefined if the property does not exist.

2. Intercept operations

  • Object.getOwnPropertyDescriptor(proxy, property)
  • Reflect.getOwnPropertyDescriptor(proxy, property)

3. Trap handler parameters

  • Target: indicates the target object.
  • Property: String key property on the referenced target object.

4. Capture invariant

If the own target.property exists and cannot be configured, the handler must return an object indicating that the property exists. If a proprietary target.property exists and is configurable, the handler must return an object representing that property is configurable. If the own target.property exists and target is not extensible, the handler must return an object representing where the property resides. If target.property does not exist and target is not extensible, the handler must return undefined to indicate that the property does not exist. If target.property does not exist, the handler cannot return an object indicating that the property is configurable

9.2.6 deleteProperty ()

The deleteProperty() trap is called in the DELETE operator. The corresponding reflection API method is reflect.deleteProperty ().

const myTarget = {};  
 
const proxy = new Proxy(myTarget,
  {  
    deleteProperty(target, property) { 
      console.log('deleteProperty()');   
      return Reflect.deleteProperty(... arguments) } } );delete proxy.foo // deleteProperty()
Copy the code

1. The return value

DeleteProperty () must return a Boolean value indicating whether the property was successfully deleted. Returns non-Boolean values are converted to booleans.

2. Intercept operations

  • delete proxy.property
  • delete proxy[property]
  • Reflect.deleteProperty(proxy, property)

3. Trap handler parameters

  • Target: indicates the target object.
  • Property: String key property on the referenced target object.

4. Capture invariant

If the own target.property exists and is not configurable, the handler cannot remove this property.

9.2.7 ownKeys ()

The ownKeys() trap is called in object.keys () and similar methods. The corresponding reflection API method is reflect.ownkeys ().

const myTarget = {};  
 
const proxy = new Proxy(myTarget, 
  {   
    ownKeys(target) {  
      console.log('ownKeys()');   
      return Reflect.ownKeys(... arguments) } } );Object.keys(proxy); // ownKeys(
Copy the code

1. The return value

OwnKeys () must return an enumerable object containing a string or symbol.

2. Intercept operations

  • Object.getOwnPropertyNames(proxy)
  • Object.getOwnPropertySymbols(proxy)
  • Object.keys(proxy)
  • Reflect.ownKeys(proxy)

3. Trap handler parameters

  • Target: indicates the target object.

4. Capture invariant

The returned enumerable object must contain all of target’s own non-configurable properties. If target is not extensible, the returned enumerable object must contain exactly its own property key.

9.2.8 getPrototypeOf ()

The getPrototypeOf() trap is called in Object.getProtoTypeof (). The corresponding reflection API method is reflect.getPrototypeof ()

const myTarget = {}; 
 
const proxy = new Proxy(myTarget,   
  {  
    getPrototypeOf(target) {     
      console.log('getPrototypeOf()'); 
      return Reflect.getPrototypeOf(... arguments) } } );Object.getPrototypeOf(proxy); // getPrototypeOf() 
Copy the code

1. The return value

GetPrototypeOf () must return an object or NULL.

2. Intercept operations

  • Object.getPrototypeOf(proxy)
  • Reflect.getPrototypeOf(proxy)
  • proxy.__proto__
  • Object.prototype.isPrototypeOf(proxy)
  • proxy instanceof Object

3. Trap handler parameters

  • Target: indicates the target object.

4. Capture invariant

If target is not extensible, the only valid return value of Object.getProtoTypeof (proxy) is the return value of Object.getProtoTypeof (target).

9.2.9 setPrototypeOf ()

SetPrototypeOf () traps are called in Object.setPrototypeof (). The corresponding reflection API method is reflect.setPrototypeof ().

const myTarget = {};  
 
const proxy = new Proxy(myTarget, 
  {  
    setPrototypeOf(target, prototype) { 
      console.log('setPrototypeOf()');   
      return Reflect.setPrototypeOf(... arguments) } } );Object.setPrototypeOf(proxy, Object); // setPrototypeOf() 
Copy the code

1. The return value

SetPrototypeOf () must return a Boolean value indicating whether the prototype assignment was successful. Returns non-Boolean values are converted to booleans.

2. Intercept operations

  • Object.setPrototypeOf(proxy)
  • Reflect.setPrototypeOf(proxy)

3. Trap handler parameters

  • Target: indicates the target object.
  • Prototype: Target substitute prototype. Null if it is a top-level prototype.

4. Capture invariant

If target is not extensible, the only valid prototype argument is the return value of Object.getProtoTypeof (target).

9.2.10 isExtensible ()

The isExtensible() trap is called in Object.isextensible (). The corresponding reflection API method is reflect.isextensible ().

const myTarget = {};  
 
const proxy = new Proxy(myTarget, 
  {  
    isExtensible(target) {  
      console.log('isExtensible()');
      return Reflect.isExtensible(... arguments) } } );Object.isExtensible(proxy); // isExtensible() 
Copy the code

1. The return value

IsExtensible () must return a Boolean value indicating whether target isExtensible. Returns non-Boolean values are converted to booleans.

2. Intercept operations

  • Object.isExtensible(proxy)
  • Reflect.isExtensible(proxy)

3. Trap handler parameters

  • Target: indicates the target object.

4. Capture invariant

If target is extensible, the handler must return true. If target is not extensible, the handler must return false.

9.2.11 preventExtensions ()

The preventExtensions() catcher is called in object.preventExtensions (). The corresponding reflection API method to Reflect preventExtensions ().

const myTarget = {};  
 
const proxy = new Proxy(myTarget, 
  {   
    preventExtensions(target) { 
      console.log('preventExtensions()');
      return Reflect.preventExtensions(... arguments) } } );Object.preventExtensions(proxy); // preventExtensions() 
Copy the code

1. The return value

PreventExtensions () must return a Boolean value indicating whether target is already unextensible. Returns non-Boolean values are converted to booleans.

2. Intercept operations

  • Object.preventExtensions(proxy)
  • Reflect.preventExtensions(proxy)

3. Trap handler parameters

  • Target: indicates the target object.

4. Capture invariant

If Object.isextensible (proxy) is false, the handler must return true.

9.2.12 the apply ()

The apply() trap is called when the function is called. The corresponding reflection API method is reflect.apply ().

const myTarget = () = > {}; 
 
const proxy = new Proxy(myTarget,
  {  
    apply(target, thisArg, ... argumentsList) { 
      console.log('apply()');    
      return Reflect.apply(... arguments) } } ); proxy();// apply() 
Copy the code

1. The return value

The return value is unlimited.

2. Intercept operations

  • proxy(… argumentsList)
  • Function.prototype.apply(thisArg, argumentsList)
  • Function.prototype.call(thisArg, … argumentsList)
  • Reflect.apply(target, thisArgument, argumentsList)

3. Trap handler parameters

  • Target: indicates the target object.
  • ThisArg: this argument when the function is called.
  • ArgumentsList: The list of arguments when a function is called

4. Capture invariant

Target must be a function object.

9.2.13 constructed ()

The construct() trap is called in the new operator. The corresponding reflection API method is reflect.construct ().

const myTarget = function() {};  
 
const proxy = new Proxy(myTarget, 
  {  
    construct(target, argumentsList, newTarget) { 
      console.log('construct()');    
      return Reflect.construct(... arguments) } } );new proxy; // construct() 
Copy the code

1. The return value

Construct () must return an object.

2. Intercept operations

  • new proxy(… argumentsList)
  • Reflect.construct(target, argumentsList, newTarget)

3. Trap handler parameters

  • Target: Target constructor.
  • ArgumentsList: The list of arguments passed to the target constructor.
  • NewTarget: the constructor called at first.

4. Capture invariant

Target must be available as a constructor.

9.3 Proxy Mode

Using agents allows you to implement some useful programming patterns in your code.

9.3.1 Tracing property Access

By capturing operations such as GET, SET, and HAS, you can know when an object property is accessed and queried. Putting an object proxy that implements the corresponding catcher into your application allows you to monitor when and where the object has been accessed:

const user = {    name: 'Jake' }; 
 
const proxy = new Proxy(user, 
  {   
    get(target, property, receiver) {   
      console.log(`Getting ${property}`); 
 
     return Reflect.get(... arguments); },set(target, property, value, receiver) { 
      console.log(`Setting ${property}=${value}`); 
      return Reflect.set(... arguments); }}); proxy.name;// Getting name
proxy.age = 27; // Setting age=27 
Copy the code

9.3.2 Hiding Attributes

The internal implementation of the agent is not visible to external code, so it is easy to hide properties on the target object.

9.3.3 Attribute Verification

Because all assignments trigger the set() catcher, assignments can be allowed or denied depending on the value assigned.

9.3.4 Function and constructor parameter verification

Similar to protecting and validating object properties, function and constructor arguments can also be reviewed

9.3.5 Data Binding and Observables

A proxy lets you link together parts of the runtime that would otherwise be unrelated. This allows you to implement patterns that allow different code to interoperate. For example, a proxied class can be bound to a global instance collection, to which all created instances can be added.

9.4 summary

Proxies are an exciting and dynamic new feature added to ECMAScript 6. Although it does not support backward compatibility, it opens up a whole new realm of JavaScript metaprogramming and abstraction that has never been seen before.

At a macro level, proxies are a transparent abstraction layer for real JavaScript objects. A proxy can define handler objects that contain traps that intercept most basic JavaScript operations and methods. In this catcher handler, you can modify the behavior of any basic operation, provided, of course, that you follow the catcher invariant.

The reflection API, which goes hand in hand with the proxy, encapsulates a set of methods that correspond to the operations intercepted by the catcher. You can think of the reflection API as a set of basic operations that underlie most JavaScript object apis.

The application scenarios of agents are infinite. Developers can use it to create coding patterns such as (but far from limited to) tracking property access, hiding properties, preventing modification or deletion of properties, function parameter validation, constructor parameter validation, data binding, and observables.

References:

JavaScript Advanced Programming (Version 4)