Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

ES6 Reflect


May 08, 2021 ES6


Table of contents


1. Overview

Reflect object, Proxy the Proxy object, is a new API that ES6 操作对象 manipulate objects. Reflect objects are designed for several purposes.

(1) Place some methods of object objects that are clearly within the language, such as Object.defineProperty, on the Reflect object. A t this stage, some methods are deployed on both Object and Reflect objects, and new methods will be deployed only on Reflect objects in the future. That is, you can get the method inside the language from the Reflect object.

(2) Modify the return results of some Object methods to make them more reasonable. For example, Object.defineProperty (obj, name, desc) throws an error when a property cannot be defined, while Reflect.defineProperty (obj, name, desc) returns false.

  1. // 老写法
  2. try {
  3. Object.defineProperty(target, property, attributes);
  4. // success
  5. } catch (e) {
  6. // failure
  7. }
  8. // 新写法
  9. if (Reflect.defineProperty(target, property, attributes)) {
  10. // success
  11. } else {
  12. // failure
  13. }

(3) Make object operations functional. Some Object operations are commandive, such as name in obj and delete obj(name), while Reflect.has (obj, name) and Reflect.deleteProperty (obj, name) make them functional behaviors.

  1. // 老写法
  2. 'assign' in Object // true
  3. // 新写法
  4. Reflect.has(Object, 'assign') // true

(4) The Reflect object Proxy of the Proxy object, and as long as it is the method of the Proxy object, you can find the method on the Reflect object. T his makes it easy for the Proxy object to call the corresponding Reflect method to complete the default behavior as the basis for modifying the behavior. That is, no matter how Proxy modifies the default behavior, you can always get the default behavior on Reflect.

  1. Proxy(target, {
  2. set: function(target, name, value, receiver) {
  3. var success = Reflect.set(target, name, value, receiver);
  4. if (success) {
  5. console.log('property ' + name + ' on ' + target + ' set to ' + value);
  6. }
  7. return success;
  8. }
  9. });

In the code above, the Proxy method intercepts the property assignment behavior of the target object. It uses the Reflect.set method to assign values to the properties of the object, ensuring that the original behavior is completed before additional functionality is deployed.

Here's another example.

  1. var loggedObj = new Proxy(obj, {
  2. get(target, name) {
  3. console.log('get', target, name);
  4. return Reflect.get(target, name);
  5. },
  6. deleteProperty(target, name) {
  7. console.log('delete' + name);
  8. return Reflect.deleteProperty(target, name);
  9. },
  10. has(target, name) {
  11. console.log('has' + name);
  12. return Reflect.has(target, name);
  13. }
  14. });

In the code above, each of the interception operations of the Proxy object (get, delete, has) calls the corresponding Reflect method internally to ensure that the native behavior performs normally. The work added is to output one line of logs for each operation.

With The Reflect object, many operations are easier to read.

  1. // 老写法
  2. Function.prototype.apply.call(Math.floor, undefined, [1.75]) // 1
  3. // 新写法
  4. Reflect.apply(Math.floor, undefined, [1.75]) // 1

2. Static method

Reflect are 13 Reflect objects.

  • Reflect.apply(target, thisArg, args)
  • Reflect.construct(target, args)
  • Reflect.get(target, name, receiver)
  • Reflect.set(target, name, value, receiver)
  • Reflect.defineProperty(target, name, desc)
  • Reflect.deleteProperty(target, name)
  • Reflect.has(target, name)
  • Reflect.ownKeys(target)
  • Reflect.isExtensible(target)
  • Reflect.preventExtensions(target)
  • Reflect.getOwnPropertyDescriptor(target, name)
  • Reflect.getPrototypeOf(target)
  • Reflect.setPrototypeOf(target, prototype)

Most of the above methods work the same as the object object's method of the same name, and it corresponds to the proxy object's method one-to-one. Here's an explanation of them.

Reflect.get(target, name, receiver)

Reflect.get finds and returns the name property of the target object, and if there is no such property, undefined is returned.

  1. var myObject = {
  2. foo: 1,
  3. bar: 2,
  4. get baz() {
  5. return this.foo + this.bar;
  6. },
  7. }
  8. Reflect.get(myObject, 'foo') // 1
  9. Reflect.get(myObject, 'bar') // 2
  10. Reflect.get(myObject, 'baz') // 3

If name property 读取函数 read function (getter), the this-bound receiver of the read function.

  1. var myObject = {
  2. foo: 1,
  3. bar: 2,
  4. get baz() {
  5. return this.foo + this.bar;
  6. },
  7. };
  8. var myReceiverObject = {
  9. foo: 4,
  10. bar: 4,
  11. };
  12. Reflect.get(myObject, 'baz', myReceiverObject) // 8

If the first argument is not an object, the Reflect.get method reports an error.

  1. Reflect.get(1, 'foo') // 报错
  2. Reflect.get(false, 'foo') // 报错

Reflect.set(target, name, value, receiver)

Reflect.set the target of the target object to equal value.

  1. var myObject = {
  2. foo: 1,
  3. set bar(value) {
  4. return this.foo = value;
  5. },
  6. }
  7. myObject.foo // 1
  8. Reflect.set(myObject, 'foo', 2);
  9. myObject.foo // 2
  10. Reflect.set(myObject, 'bar', 3)
  11. myObject.foo // 3

If the name property sets the assignment function, the assignment function's this binding receiver.

  1. var myObject = {
  2. foo: 4,
  3. set bar(value) {
  4. return this.foo = value;
  5. },
  6. };
  7. var myReceiverObject = {
  8. foo: 0,
  9. };
  10. Reflect.set(myObject, 'bar', 1, myReceiverObject);
  11. myObject.foo // 4
  12. myReceiverObject.foo // 1

Note that If the Proxy object is used in association with the Reflect object, which intercepts the assignment, which completes the default behavior of the assignment, and the receiver is passed in, Reflect.set triggers the Proxy.defineProperty intercept.

  1. let p = {
  2. a: 'a'
  3. };
  4. let handler = {
  5. set(target, key, value, receiver) {
  6. console.log('set');
  7. Reflect.set(target, key, value, receiver)
  8. },
  9. defineProperty(target, key, attribute) {
  10. console.log('defineProperty');
  11. Reflect.defineProperty(target, key, attribute);
  12. }
  13. };
  14. let obj = new Proxy(p, handler);
  15. obj.a = 'A';
  16. // set
  17. // defineProperty

In the code above, The Proxy.set intercept uses Reflect.set and is passed in to receiver, triggering the Proxy.defineProperty intercept. T his is because the receiver parameter of Proxy.set always points to the current Proxy instance (i.e., the obj of the example above), and once Reflect.set is passed in to receiver, the property is assigned to the receiver (that is, obj), causing the defineProperty blocking to be triggered. If Reflect.set does not pass in receiver, the defineProperty intercept is not triggered.

  1. let p = {
  2. a: 'a'
  3. };
  4. let handler = {
  5. set(target, key, value, receiver) {
  6. console.log('set');
  7. Reflect.set(target, key, value)
  8. },
  9. defineProperty(target, key, attribute) {
  10. console.log('defineProperty');
  11. Reflect.defineProperty(target, key, attribute);
  12. }
  13. };
  14. let obj = new Proxy(p, handler);
  15. obj.a = 'A';
  16. // set

If the first argument is not an object, Reflect.set will report an error.

  1. Reflect.set(1, 'foo', {}) // 报错
  2. Reflect.set(false, 'foo', {}) // 报错

Reflect.has(obj, name)

Reflect.has method corresponds to the in operator inside in name in obj

  1. var myObject = {
  2. foo: 1,
  3. };
  4. // 旧写法
  5. 'foo' in myObject // true
  6. // 新写法
  7. Reflect.has(myObject, 'foo') // true

If the first argument of the Reflect.has() method is not an object, an error is reported.

Reflect.deleteProperty(obj, name)

Reflect.deleteProperty is equivalent delete obj[name] to remove the properties of an object.

  1. const myObj = { foo: 'bar' };
  2. // 旧写法
  3. delete myObj.foo;
  4. // 新写法
  5. Reflect.deleteProperty(myObj, 'foo');

The method returns a Boolean value. If the deletion succeeds, or if the deleted property does not exist, true is returned;

If the first argument of the Reflect.deleteProperty() method is not an object, an error is reported.

Reflect.construct(target, args)

Reflect.construct is equivalent new target(...args) which provides a way to call constructors without using new.

  1. function Greeting(name) {
  2. this.name = name;
  3. }
  4. // new 的写法
  5. const instance = new Greeting('张三');
  6. // Reflect.construct 的写法
  7. const instance = Reflect.construct(Greeting, ['张三']);

If the first argument of the Reflect.construct() method is not a function, an error is reported.

Reflect.getPrototypeOf(obj)

Reflect.getPrototypeOf used to __proto__ of an object, corresponding to Object.getPrototypeOf (obj).

  1. const myObj = new FancyThing();
  2. // 旧写法
  3. Object.getPrototypeOf(myObj) === FancyThing.prototype;
  4. // 新写法
  5. Reflect.getPrototypeOf(myObj) === FancyThing.prototype;

One of the differences between Reflect.getPrototypeOf and Object.getPrototypeOf is that if the parameter is not an object, Object.getPrototypeOf converts the parameter into an object and then runs it, while Reflect.getPrototypeOf will report an error.

  1. Object.getPrototypeOf(1) // Number {[[PrimitiveValue]]: 0}
  2. Reflect.getPrototypeOf(1) // 报错

Reflect.setPrototypeOf(obj, newProto)

Reflect.setPrototypeOf 目标对象 method is used to 原型 the prototype of the target object, corresponding to the Object.setPrototypeOf (obj, newProto) method. It returns a Boolean value to indicate whether the setting was successful.

  1. const myObj = {};
  2. // 旧写法
  3. Object.setPrototypeOf(myObj, Array.prototype);
  4. // 新写法
  5. Reflect.setPrototypeOf(myObj, Array.prototype);
  6. myObj.length // 0

If the prototype of the target object cannot be set (for example, the target object does not extend), the Reflect.setPrototypeOf method returns false.

  1. Reflect.setPrototypeOf({}, null)
  2. // true
  3. Reflect.setPrototypeOf(Object.freeze({}), null)
  4. // false

If the first argument is not an object, Object.setPrototypeOf returns the first argument itself, and Reflect.setPrototypeOf will report an error.

  1. Object.setPrototypeOf(1, {})
  2. // 1
  3. Reflect.setPrototypeOf(1, {})
  4. // TypeError: Reflect.setPrototypeOf called on non-object

If the first argument is undefined or null, Object.setPrototypeOf and Reflect.setPrototypeOf will report errors.

  1. Object.setPrototypeOf(null, {})
  2. // TypeError: Object.setPrototypeOf called on null or undefined
  3. Reflect.setPrototypeOf(null, {})
  4. // TypeError: Reflect.setPrototypeOf called on non-object

Reflect.apply(func, thisArg, args)

Reflect.apply method is Function.prototype.apply.call(func, thisArg, args) which is used to bind this object and execute a given function.

In general, if you want to bind this object of a function, you can write fn.apply (obj, args) like this, but if the function defines its own apply method, it can only be written as Function.prototype.apply.call (fn, obj, args), which can be simplified with a Reflect object.

  1. const ages = [11, 33, 12, 54, 18, 96];
  2. // 旧写法
  3. const youngest = Math.min.apply(Math, ages);
  4. const oldest = Math.max.apply(Math, ages);
  5. const type = Object.prototype.toString.call(youngest);
  6. // 新写法
  7. const youngest = Reflect.apply(Math.min, Math, ages);
  8. const oldest = Reflect.apply(Math.max, Math, ages);
  9. const type = Reflect.apply(Object.prototype.toString, youngest, []);

Reflect.defineProperty(target, propertyKey, attributes)

Reflect.defineProperty is basically Object.defineProperty and is used to define properties for objects. In the future, the latter will be phased out, so use Reflect.defineProperty instead of it from now on.

  1. function MyDate() {
  2. /*…*/
  3. }
  4. // 旧写法
  5. Object.defineProperty(MyDate, 'now', {
  6. value: () => Date.now()
  7. });
  8. // 新写法
  9. Reflect.defineProperty(MyDate, 'now', {
  10. value: () => Date.now()
  11. });

If the first argument of Reflect.defineProperty is not an object, an error is thrown, such as Reflect.defineProperty (1, 'foo').

This method can be used in conjunction with Proxy.defineProperty.

  1. const p = new Proxy({}, {
  2. defineProperty(target, prop, descriptor) {
  3. console.log(descriptor);
  4. return Reflect.defineProperty(target, prop, descriptor);
  5. }
  6. });
  7. p.foo = 'bar';
  8. // {value: "bar", writable: true, enumerable: true, configurable: true}
  9. p.foo // "bar"

In the code above, Proxy.defineProperty sets an intercept on property assignments, and then uses Reflect.defineProperty to complete the assignment.

Reflect.getOwnPropertyDescriptor(target, propertyKey)

Reflect.getOwnPropertyDescriptor basically Object.getOwnPropertyDescriptor is used to get a description object for a specified property and will replace the latter in the future.

  1. var myObject = {};
  2. Object.defineProperty(myObject, 'hidden', {
  3. value: true,
  4. enumerable: false,
  5. });
  6. // 旧写法
  7. var theDescriptor = Object.getOwnPropertyDescriptor(myObject, 'hidden');
  8. // 新写法
  9. var theDescriptor = Reflect.getOwnPropertyDescriptor(myObject, 'hidden');

One of the differences between Reflect.getOwnPropertyDescriptor and Object.getOwnPropertyDescriptor is that if the first argument is not an object, Object.getOwnPropertyDescriptor (1, 'foo') does not report an error and returns undefined instead Reflect.getOwnPropertyDescriptor (1, 'foo') throws an error indicating that the argument is illegal.

Reflect.isExtensible (target)

Reflect.isExtensible corresponds Object.isExtensible a 布尔值 that indicates whether the current object is 是否可扩展

  1. const myObject = {};
  2. // 旧写法
  3. Object.isExtensible(myObject) // true
  4. // 新写法
  5. Reflect.isExtensible(myObject) // true

If the argument is not an object, Object.isExtensible returns false because the non-object is inherently unextendable, and Reflect.isExtensible reports an error.

  1. Object.isExtensible(1) // false
  2. Reflect.isExtensible(1) // 报错

Reflect.preventExtensions(target)

Reflect.preventExtensions Object.preventExtensions which makes an object 不可扩展 It returns a Boolean value to indicate whether the operation was successful.

  1. var myObject = {};
  2. // 旧写法
  3. Object.preventExtensions(myObject) // Object {}
  4. // 新写法
  5. Reflect.preventExtensions(myObject) // true

If the argument is not an object, Object.preventExtensions reports an error in the ES5 environment, an incoming argument is returned in the ES6 environment, and Reflect.preventExtensions reports an error.

  1. // ES5 环境
  2. Object.preventExtensions(1) // 报错
  3. // ES6 环境
  4. Object.preventExtensions(1) // 1
  5. // 新写法
  6. Reflect.preventExtensions(1) // 报错

Reflect.ownKeys (target)

Reflect.ownKeys used to return all the properties of an object, basically the same as Object.getOwnPropertyNames and Object.getOwnPropertySymbols

  1. var myObject = {
  2. foo: 1,
  3. bar: 2,
  4. [Symbol.for('baz')]: 3,
  5. [Symbol.for('bing')]: 4,
  6. };
  7. // 旧写法
  8. Object.getOwnPropertyNames(myObject)
  9. // ['foo', 'bar']
  10. Object.getOwnPropertySymbols(myObject)
  11. //[Symbol(baz), Symbol(bing)]
  12. // 新写法
  13. Reflect.ownKeys(myObject)
  14. // ['foo', 'bar', Symbol(baz), Symbol(bing)]

If the first argument of the Reflect.ownKeys() method is not an object, an error is reported.

3. Example: Use Proxy to implement observer mode

Observer mode 观察者模式 to a function that 自动观察数据对象 and executes automatically when the object changes.

  1. const person = observable({
  2. name: '张三',
  3. age: 20
  4. });
  5. function print() {
  6. console.log( ${person.name}, ${person.age} )
  7. }
  8. observe(print);
  9. person.name = '李四';
  10. // 输出
  11. // 李四, 20

In the above code, the data object person is the observation target, and the function print is the observer. Once the data object changes, the print is executed automatically.

Below, use Proxy to write the simplest implementation of an observer pattern, which is to implement both observable and observe functions. The idea is that the observable function returns a Proxy proxy for the original object, intercepts assignments, and triggers individual functions that act as observers.

  1. const queuedObservers = new Set();
  2. const observe = fn => queuedObservers.add(fn);
  3. const observable = obj => new Proxy(obj, {set});
  4. function set(target, key, value, receiver) {
  5. const result = Reflect.set(target, key, value, receiver);
  6. queuedObservers.forEach(observer => observer());
  7. return result;
  8. }

In the above code, a Set collection is Set 集合 and all observer functions are placed in the set. T he observable function then returns the agent of the original object, blocking the assignment operation. In the intercept function set, all observers are automatically executed.