• ES6’s new proxies and reflections give developers the ability to intercept and embed additional behavior into basic operations.
    • The target object can be defined with an associated proxy object that can be used as an abstract target object.
    • Before various operations are performed on the target object that affect the target, these operations can be controlled in the proxy object.
    • Does not translate to previous code, is irreplaceable and is only available on supported platforms.

1. Agency basis

  • A proxy is an abstraction of the target object, similar to a C++ pointer in that it can be used as a stand-in for the target object, but completely independent of the target object. Target objects can be manipulated either directly or through proxies.

1. Create an empty proxy

  • The simplest proxy is to do nothing. By default any operation on a proxy object is propagated to the target object. The proxy object associated with the target object can be used in the same way wherever it can be used.

  • Proxies are created using the Proxy constructor.

    • Two parameters, the target object and the handler object. If you don’t have one, you make a mistake.
    • Creating an empty proxy can pass a simple object literal as a handler object
    const target = {
      id: 'target'
    }
    const handler = {}
    const proxy = new Proxy(target, handler)
    
    // The id attribute accesses the same value
    target.id // target
    proxy.id // target
    
    // Assign a value to the target attribute
    target.id = 'foo'
    target.id // foo
    proxy.id // foo
    
    // Assigning to the proxy object is passed to the target object
    proxy.id = 'bar'
    target.id // bar
    proxy.id // bar
    
    The hasOwnProperty() method is applied to the target object in both places
    target.hasOwnProperty('id') // true
    proxy.hasOwnProperty('id') // true
    
    / / Proxy. The prototype is undefined
    // So the instanceof operator cannot be used
    target instanceof Proxy / / an error
    proxy instanceof Proxy / / an error
    
    target === proxy // false can be distinguished
    Copy the code

2. Define the catcher

  • The main purpose is to be able to define traps

  • A catcher is an “interceptor for basic operations” defined in a handler object.

  • Each handler object can contain zero or more catchers.

  • Each catcher 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 behavior by calling the catcher function before the operations propagate to the target object.

  • Catcher, operating system concept, a synchronous interrupt in a program flow that suspends the program flow, executes a subprocess, and then returns to the original program flow

    const target = {
      foo: 'bar'
    }
    const handler = {
      // The trap handles the program with a method name key
      get() {
        return 'handler override'}}const proxy = new Proxy(target, handler)
    target.foo // bar
    proxy.foo // handler override
    
    target['foo'] //bar
    proxy['foo'] // handler override
    
    Object.create(target)['foo'] //bar
    Object.create(proxy)['foo'] // handler override
    Copy the code
    • When a proxy object performs a get() operation, the defined GET () catcher is fired.
    • The method that get() fires
      • proxy[property]
      • proxy.prototype
      • Object. The create (proxy) [property], etc
    • Emitted only when the proxy object is executed.

3. Capture parameters and reflection APIS

  • All catchers have access to parameters based on which the original behavior of the captured method can be reconstructed.

  • The GET () trap receives the target object, the property to be queried, and the proxy object

    const target = {
      foo: 'bar'
    }
    const handler = {
      get(trapTarget, property, receiver) {
        console.log(trapTarget === target)
        console.log(property)
        console.log(receiver === proxy)
      }
    }
    const proxy = new Proxy(target, handler)
    proxy.foo
    // true
    // foo
    // true
    Copy the code
    const target = {
      foo: 'bar'
    }
    const handler = {
      get(trapTarget, property, receiver) {
        return trapTarget[property]
      }
    }
    const proxy = new Proxy(target, handler)
    target.foo // bar
    proxy.foo // bar
    Copy the code
  • The original action can be reconstructed by calling a method of the same name on the global Reflect object

  • All catchable methods in the handler object have corresponding Reflection API methods.

  • These methods have the same name and function signature as those intercepted by the catcher. Also has the same behavior as the intercepted method

    const target = {
      foo: 'bar'
    }
    const handler = {
      get() {
        return Reflect.get(... arguments) } }const proxy = new Proxy(target, handler)
    target.foo // bar
    proxy.foo // bar
    
    // A simpler way to write it.const handler = {
      get: Reflect.get
    }
    ...
    Copy the code
  • Create an empty proxy that captures all methods and forwards each method to the corresponding reflection API without defining a handler object

    const target = {
      foo: 'bar'
    }
    const proxy = new Proxy(target, Reflect)
    Copy the code
  • Modification capture method

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

4. Capture invariant

  • The behavior of the capture handler must follow the “capture invariant”

  • The “catcher invariant” varies from method to method to prevent too much abnormal behavior

  • If the target object has a non-configurable, non-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)
    proxy.foo // TypeError
    Copy the code

5. Revocable agency

  • Sometimes it is necessary to break the connection between the Proxy object and the target object, and the normal Proxy connection created using new Proxy() persists for the duration of the declaration cycle of the Proxy object.

  • Revoable method exposed by Proxy supports revoking the association between Proxy objects and target objects

  • Revoking an agent is irreversible

  • The revoke() function is idempotent and results no matter how many times it is called.

  • Calling the proxy after it has been revoked raises TypeError

  • The undo and proxy functions are generated at the same time.

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

6. Practical reflection API

  • In some cases reflection apis are preferred

Reflection API and object API

  1. Reflection apis are not limited to capture handlers

  2. Most reflection apis have corresponding methods on the Obejct type

    The Object method is suitable for general purpose programs, while the reflection method is suitable for fine-grained Object control and manipulation

2. Status markers

  • Many reflection methods return a Boolean value for a “status marker” indicating whether the intended operation was successful. Sometimes, status flags are more useful than reflection apis that return modified objects or throw errors.

    // init
    const o = {}
    try {
      Object.defineProperty(o, 'foo'.'bar')
      console.log('success')}catch(e) {
      console.log('failure')}/ / after the refactoring
    const o = {}
    if(Reflect.defineProperty(o, 'foo', {value:'bar'{}))console.log('success')}else {
      console.log('failure')}Copy the code
  • Reflection that provides a status marker:

    • Reflect.defineProperty()
    • Reflect.preventExtensions()
    • Reflect.setProptotypeOf()
    • Reflect.set()
    • Reflect.deleteProperty()

3. Replace operators with first-class functions

  • The following reflection methods are operations that can only be done by 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 Apply, the called Function may also have its own Apply attribute defined, and you can get around this problem by using the Apply method defined on the Function prototype

    Function.prototype.apply.call(myFunc, thisVal, argumentList)
    Copy the code
    • This can be avoided using reflect.apply

      Reflect.apply(myFunc, thisVal, argumentList)
      Copy the code

7. Agent another agent

  • A proxy can intercept the operations of the reflection API, and a proxy can be created entirely to proxy another proxy.

    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) } }) secondProxy.foo// second proxy
    // first proxy
    // bar
    Copy the code

8. Problems and deficiencies of agency

1. This in the agent

const target = {
  thisValEqualProxy() {
    return this === proxy
  }
}

const proxy = new Proxy(target, {})
target.thisValEqualProxy() // false
proxy.thisValEqualProxy() // true
Copy the code
  • If the target object depends on the object identity, there may be unexpected problems

    const wm = new WeakMap(a)class User {
      constructor(userId) {
        wm.set(this, userId)
      }
      set id(userId) {
        wm.set(this, userId)
      }
      get id() {
        return wm.get(this)}}// Since this implementation relies on the object identity of the User instance, there is a problem if the instance is proxied
    const user = new User(123)
    user.id = 123
    const userInstanceProxy = new Proxy(user, {})
    userInstanceProxy.id // undefined
    Copy the code
    • Because the User instance uses the target object as a WeakMap key, the proxy object tries to get the instance from itself. The agent needs to be reconfigured to change the agent User instance to the agent User class itself

      const UserClassProxy = new Proxy(User, {})
      const proxyUser = new UserClassProxy(456)
      proxyUser.id / / 456
      Copy the code

2. Agent and internal slot

  • Some built-in types may rely on mechanisms beyond the control of the agent

    // Date depends on the [[NumberDate]] internal slot on this, but the proxy object does not
    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

2. Proxy capture and reflection methods

  • Several JaveScript operations invoke the same catcher handler
  • For any operation on a proxy object, only one capture handler is invoked

1, the get ()

  • Called in an operation to get a property value, corresponding to 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. Return value

Return value unlimited

2. Interception operation

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

3. The catcher handler function

  • Target Target object
  • The string property on the target object referenced by the property (also known as symbol)
  • Receiver A proxy object or an object that inherits a proxy object

4. Capture invariant

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

2, the set ()

  • The set() trap is called in the operation that sets the property value, corresponding to 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. Return value

Return true for success, false for failure, strict mode TypeError

2. Interception operation

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

3. The catcher handler function

  • Target Target object
  • The string property on the target object referenced by the property (also known as symbol)
  • Value Specifies the value to be assigned to the attribute
  • Receiver Receives the object initially assigned

4. Capture invariant

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

3, from the ()

  • The has() trap is called in the in operator, corresponding to 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. Return value

Has () must return a Boolean indicating whether the attribute exists, and returns a non-Boolean value that is converted to a Boolean value

2. Interception operation

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

3. The catcher handler function

  • Target Target object
  • The string property on the target object referenced by the property (also known as symbol)

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

4, defineProperty ()

  • DefineProperty () is called in Object.defineProperty(), corresponding to 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. Return value

DefineProperty () must return a Boolean value indicating whether the property was successfully defined, and returns a non-Boolean value are converted to a Boolean value

2. Interception operation

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

3. The catcher handler function

  • Target Target object
  • The string property on the target object referenced by the property (also known as symbol)
  • 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

5, getOwnPropertyDescriptor ()

  • GetOwnPropertyDescriptor () will capture the Object. GetOwnPropertyDescriptor () is called, corresponding 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. Return value

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

2. Interception operation

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

3. The catcher handler function

  • Target Target object
  • The string property on the target object referenced by the property (also known as symbol)

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 indicating that the property exists.
  • 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

6, deleteProperty ()

  • The deleteProperty() trap is called in the delete operator, corresponding to 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. Return value

DeleteProperty () must return a Boolean value indicating whether the property was deleted successfully. Non-boolean values returned are converted to Booleans

2. Interception operation

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

3. The catcher handler function

  • Target Target object
  • The string property on the target object referenced by the property (also known as symbol)

4. Capture invariant

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

7, ownKeys ()

  • The ownKeys() trap is called in object.keys () and similar methods, corresponding to 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. Return value

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

2. Interception operation

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

3. The catcher handler function

  • Target 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

8, getPrototypeOf ()

  • GetPrototypeOf () is called in Object.getPrototypeof (), corresponding to 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. Return value

GetPrototypeOf () must return an object or NULL

2. Interception operation

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

3. The catcher handler function

  • Target 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 (proxy)

9, setPrototypeOf ()

  • The setPrototypeOf() trap is called in Object.setPrototypeof (), corresponding to 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. Return value

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

2. Interception operation

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

3. The catcher handler function

  • Target Target object
  • Prototype Target replaces the prototype, or 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)

10, isExtensible ()

  • The isExtensible() trap is called in Object.isextensible (), corresponding to 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. Return value

IsExtensible () must return a Boolean indicating whether target isExtensible or not; returning a non-boolean value is converted to a Boolean value

2. Interception operation

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

3. The catcher handler function

  • Target Target object

4. Capture invariant

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

11, preventExtensions ()

  • PreventExtensions () will capture the Object. PreventExtensions () is called, corresponding 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. Return value

PreventExtensions () must return a Boolean indicating whether target is already unextensible, returning a non-Boolean value will be converted to a Boolean value

2. Interception operation

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

3. The catcher handler function

  • Target Target object

4. Capture invariant

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

12, the apply ()

  • The apply() trap is called when the function is called, corresponding to reflect.apply ()

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

1. Return value

Return value unlimited

2. Interception operation

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

3. The catcher handler function

  • Target Target object
  • ThisArg this argument when the function is called
  • ArgumentsList Specifies the list of arguments when a function is called

4. Capture invariant

  • Target must be a function object

13, the construct ()

  • The construct() trap is called in the new operator, corresponding to reflect.construct ()

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

1. Return value

Construct () must return an object

2. Interception operation

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

3. The catcher handler function

  • Target Constructs the target object
  • ArgumentsList The list of arguments passed to the target constructor
  • NewTarget The constructor that was originally called

4. Capture invariant

  • Target must be available as a constructor

3. Proxy mode

Track property access

  • Get, set, and HAS know when object properties are accessed and queried

    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

2. Hide attributes

  • The broker’s internal implementation is invisible to external code, and it is easy to hide properties on the target object

    const hiddenProperties = ['foo'.'bar'];
    const targetObject = {
    	foo: 1.bar: 2.baz: 3
    };
    const proxy = new Proxy(targetObject, {
    	get(target, property) {
    		if (hiddenProperties.includes(property)) {
    			return undefined;
    		} else {
    			return Reflect.get(...arguments);
    		}
    	},
    	has(target, property) { 
    		if (hiddenProperties.includes(property)) {
    			return false;
    		} else {
    			return Reflect.has(... arguments); }}});// get()
    console.log(proxy.foo); // undefined
    console.log(proxy.bar); // undefined
    console.log(proxy.baz); / / 3
    // has()
    console.log('foo' in proxy); // false
    console.log('bar' in proxy); // false
    console.log('baz' in proxy); // true 
    Copy the code

3. Attribute verification

  • All assignments fire set(), and assignments can be allowed or not, depending on the value assigned

    const target = {
    	onlyNumbersGoHere: 0
    };
    const proxy = new Proxy(target, {
    	set(target, property, value) {
    		if (typeofvalue ! = ='number') {
    			return false;
    		} else {
    			return Reflect.set(... arguments); }}}); proxy.onlyNumbersGoHere =1;
    console.log(proxy.onlyNumbersGoHere); / / 1
    proxy.onlyNumbersGoHere = '2';
    console.log(proxy.onlyNumbersGoHere); / / 1
    Copy the code

4. Function and constructor parameter validation

  • You can make functions accept only certain types of values

    function median(. nums) {
      return nums.sort()[Math.floor(nums.length / 2)];
    }
    const proxy = new Proxy(median, {
      apply(target, thisArg, argumentsList) {
        for (const arg of argumentsList) {
          if (typeofarg ! = ='number') {
            throw 'Non-number argument provided'; }}return Reflect.apply(...arguments);
      }
    });
    console.log(proxy(4.7.1)); / / 4
    console.log(proxy(4.'7'.1));
    // Error: Non-number argument provided 
    Copy the code
  • You can require that the constructor be passed parameters on instantiation

    class User {
      constructor(id) {
        this.id_ = id; }}const proxy = new Proxy(User, {
      construct(target, argumentsList, newTarget) {
        if (argumentsList[0= = =undefined) {
          throw 'User cannot be instantiated without id';
        } else {
          return Reflect.construct(... arguments); }}});new proxy(1);
    new proxy();
    // Error: User cannot be instantiated without id
    Copy the code

5. Data binding and observables

  • Connecting previously unrelated parts of the runtime to implement patterns that allow different code to interoperate

  • A proxied class can be bound to a global instance collection to which all created instances are added

    const userList = []
    class User {
      constructor(name) {
        this._name = name
      }
    }
    const proxy = new Proxy(User, {
      construct() {
        const newUser = Reflect.construct(... arguments) userList.push(newUser)return newUser
      }
    })
    
    new proxy('John')
    new proxy('Jacob')
    new proxy('Jingleheimerschmidt')
    userList // [User {}, User {}, User {}]
    Copy the code
  • You can also bind the collection to an event dispatcher that sends a message each time a new instance is inserted

    const userList = []
    
    function emit(newValue) {
      console.log(newValue)
    }
    const proxy = new Proxy(User, {
      set(target, property, value, reveiver) {
        const result = Reflect.set(... arguments)if(result) {
    			emit(Reflect.get(target, property, reveiver))
        }
        return result
      }
    })
    
    proxy.push('John')
    // John
    proxy.push('Jacob')
    // Jacob
    Copy the code