An overview of the

Reflect objects, like Proxy objects, are a new API provided by ES6 for manipulating objects. The Reflect object is designed for several purposes.

(1) Put some methods of Object that are clearly internal to the language (such as Object.defineProperty) on Reflect. At this stage, some methods are deployed on both Object and Reflect objects, and future new methods will only be deployed on Reflect objects. That is, from the Reflect object you can get the methods inside the language.

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

// Try {object.defineProperty (target, property, attributes); // success} catch (e) {// failure} // new if (reflect.defineProperty (target, property, attributes)) { // success } else { // failure }Copy the code

(3) Make all Object operations functions. Some Object operations are imperative, such as name in obj and delete obj[name], while reflect.has (obj, name) and reflect.deleteProperty (obj, name) make them functional behavior.

// Reflect. Has (Object, 'assign') // trueCopy the code

(4) Reflect object method and Proxy object method one by one, as long as the Proxy object method, you can find the corresponding method on Reflect object. This allows the Proxy object to easily call the corresponding Reflect method, completing the default behavior as a basis for modifying the behavior. That is, no matter how the Proxy changes the default behavior, you can always get the default behavior in Reflect.

Proxy(target, { set: function(target, name, value, receiver) { var success = Reflect.set(target, name, value, receiver); if (success) { console.log('property ' + name + ' on ' + target + ' set to ' + value); } return success; }});Copy the code

In the code above, the Proxy method intercepts the attribute 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 complete, and then deploys additional functionality.

Here’s another example.

var loggedObj = new Proxy(obj, { get(target, name) { console.log('get', target, name); return Reflect.get(target, name); }, deleteProperty(target, name) { console.log('delete' + name); return Reflect.deleteProperty(target, name); }, has(target, name) { console.log('has' + name); return Reflect.has(target, name); }});Copy the code

In the above code, the corresponding Reflect method is called internally for each interception operation (GET, DELETE, has) of the Proxy object, ensuring that the native behavior is properly executed. The added job is to print a log line for each operation.

With the Reflect object, many operations will be easier to read.

. / / the old writing the Function prototype. Apply. Call (Math) floor, undefined, [1.75]) / / 1 / / new writing Reflect. Apply (Math) floor, undefined, / / 1 [1.75])Copy the code

A static method

There are 13 static methods on the Reflect object.

  • 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 functions of these methods are the same as those of methods of the same name on Object objects, and they correspond to the methods of Proxy objects one by one. Here is an explanation of them.

Reflect.get(target, name, receiver)

The reflect. get method looks for and returns the name property of the target object, or undefined if it doesn’t exist.

var myObject = {
  foo: 1,
  bar: 2,
  get baz() {
    return this.foo + this.bar;
  },
}

Reflect.get(myObject, 'foo') // 1
Reflect.get(myObject, 'bar') // 2
Reflect.get(myObject, 'baz') // 3
Copy the code

If the name attribute deploys a getter, the function’s this binding receiver is read.

var myObject = { foo: 1, bar: 2, get baz() { return this.foo + this.bar; }}; var myReceiverObject = { foo: 4, bar: 4, }; Reflect.get(myObject, 'baz', myReceiverObject) // 8Copy the code

The reflect. get method will report an error if the first argument is not an object.

Reflect.get(1, 'foo') // error reflect.get (false, 'foo') // errorCopy the code

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

The reflect. set method sets the name property of the target object to value.

var myObject = {
  foo: 1,
  set bar(value) {
    return this.foo = value;
  },
}

myObject.foo // 1

Reflect.set(myObject, 'foo', 2);
myObject.foo // 2

Reflect.set(myObject, 'bar', 3)
myObject.foo // 3
Copy the code

If the name attribute sets the assignment function, the assignment function’s this binding receiver.

var myObject = { foo: 4, set bar(value) { return this.foo = value; }}; var myReceiverObject = { foo: 0, }; Reflect.set(myObject, 'bar', 1, myReceiverObject); myObject.foo // 4 myReceiverObject.foo // 1Copy the code

Note that if a Proxy object is used in conjunction with a Reflect object, the former intercepts assignment, the latter does the default assignment, and the receiver is passed in, then reflect.set triggers proxy.defineProperty interception.

let p = { a: 'a' }; let handler = { set(target, key, value, receiver) { console.log('set'); Reflect.set(target, key, value, receiver) }, defineProperty(target, key, attribute) { console.log('defineProperty'); Reflect.defineProperty(target, key, attribute); }}; let obj = new Proxy(p, handler); obj.a = 'A'; // set // definePropertyCopy the code

In the above code, reflect. set is used in the proxy.set interception and passed in the receiver, causing proxy.defineProperty interception to be triggered. This is because proxy.set’s receiver parameter always points to the current Proxy instance (obj in the above example), whereas reflect.set, once passed to the receiver, assigns properties to the receiver (obj), causing a defineProperty interception to be triggered. If reflect. set is not passed to the receiver, the defineProperty interception is not triggered.

let p = { a: 'a' }; let handler = { set(target, key, value, receiver) { console.log('set'); Reflect.set(target, key, value) }, defineProperty(target, key, attribute) { console.log('defineProperty'); Reflect.defineProperty(target, key, attribute); }}; let obj = new Proxy(p, handler); obj.a = 'A'; // setCopy the code

Reflect.set will get an error if the first argument is not an object.

Reflect the set (1, 'foo' {}) / / an error Reflect. Set (false, 'foo' {}) / / an errorCopy the code

Reflect.has(obj, name)

The reflect. has method corresponds to the in operator in name in obj.

var myObject = { foo: 1, }; // Reflect. Has (myObject, 'foo') // trueCopy the code

An error is reported if the first argument to the reflect.has () method is not an object.

Reflect.deleteProperty(obj, name)

The reflect. deleteProperty method is equivalent to delete obj[name], used to delete an object’s property.

const myObj = { foo: 'bar' }; // delete myobj.foo; // Reflect. DeleteProperty (myObj, 'foo');Copy the code

This method returns a Boolean value. Returns true if the deletion succeeded or if the deleted attribute does not exist; Failed to delete, the deleted property still exists, return false.

An error is reported if the first argument to the reflect.deleteProperty () method is not an object.

Reflect.construct(target, args)

The reflect. construct method is equivalent to new target(… Args), which provides a way to call the constructor without using new.

function Greeting(name) {
  this.name = name;
}

// new 的写法
const instance = new Greeting('张三');

// Reflect.construct 的写法
const instance = Reflect.construct(Greeting, ['张三']);
Copy the code

An error is reported if the reflect.construct () method’s first argument is not a function.

Reflect.getPrototypeOf(obj)

The reflect.getProtoTypeof method is used to read the __proto__ property of an Object, corresponding to Object.getProtoTypeof (obj).

const myObj = new FancyThing(); GetPrototypeOf (myObj) === FancyThing. Prototype; // Reflect. GetPrototypeOf (myObj) === FancyThing. Prototype;Copy the code

One difference between reflect.getProtoTypeof and Object.getProtoTypeof is that if the parameter is not an Object, Object.getProtoTypeof will turn the parameter into an Object and then run it. Reflect.getprototypeof returns an error.

Object.getprototypeof (1) // Number {[PrimitiveValue]]: 0} Reflect.getProtoTypeof (1) // ErrorCopy the code

Reflect.setPrototypeOf(obj, newProto)

The reflect.setPrototypeof method is used to set the prototype of the target Object, corresponding to the Object.setPrototypeof (obj, newProto) method. It returns a Boolean value indicating whether the setting was successful.

const myObj = {}; SetPrototypeOf (myObj, array.prototype); // Reflect. SetPrototypeOf (myObj, array.prototype); myObj.length // 0Copy the code

The reflect.setPrototypeof method returns false if the target object’s prototype cannot be set (for example, the target object has no extension).

Reflect.setPrototypeOf({}, null)
// true
Reflect.setPrototypeOf(Object.freeze({}), null)
// false
Copy the code

If the first argument is not an Object, Object.setPrototypeof returns the first argument itself, and reflect.setPrototypeof returns an error.

Object.setPrototypeOf(1, {})
// 1

Reflect.setPrototypeOf(1, {})
// TypeError: Reflect.setPrototypeOf called on non-object
Copy the code

Object.setPrototypeOf and reflect. setPrototypeOf will both fail if the first argument is undefined or null.

Object.setPrototypeOf(null, {})
// TypeError: Object.setPrototypeOf called on null or undefined

Reflect.setPrototypeOf(null, {})
// TypeError: Reflect.setPrototypeOf called on non-object
Copy the code

Reflect.apply(func, thisArg, args)

Reflect the apply method is equivalent to the Function. The prototype. Apply. Call (func, thisArg, args), used for binding after this object to perform a given Function.

In general, if you want to bind a Function of this object, you can write fn. Apply (obj, args), but if a Function defined my own way to apply, you can only write the Function. The prototype. Apply. Call (fn, obj, args), Using the Reflect object simplifies this operation.

const ages = [11, 33, 12, 54, 18, 96]; // Const youngest = math.min. Apply (Math, ages); const oldest = Math.max.apply(Math, ages); const type = Object.prototype.toString.call(youngest); // Const youngest = Reflect. Apply (math.min, Math, ages); const oldest = Reflect.apply(Math.max, Math, ages); const type = Reflect.apply(Object.prototype.toString, youngest, []);Copy the code

Reflect.defineProperty(target, propertyKey, attributes)

The reflect.defineProperty method is basically equivalent to object.defineProperty and is used to define attributes for an Object. The latter will be phased out in the future, so use Reflect.defineProperty instead from now on.

The function MyDate () {/ *... DefineProperty (MyDate, 'now', {value: () => date.now ()}); // reflect.defineProperty (MyDate, 'now', {value: () => date.now ()});Copy the code

An error is thrown if the first argument to reflect.defineProperty is not an object, such as reflect.defineProperty (1, ‘foo’).

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

const p = new Proxy({}, { defineProperty(target, prop, descriptor) { console.log(descriptor); return Reflect.defineProperty(target, prop, descriptor); }}); p.foo = 'bar'; // {value: "bar", writable: true, enumerable: true, configurable: true} p.foo // "bar"Copy the code

In the code above, proxy.defineProperty sets the interception for attribute assignment and then completes the assignment using Reflect.defineProperty.

Reflect.getOwnPropertyDescriptor(target, propertyKey)

Reflect. GetOwnPropertyDescriptor basic equivalent to the Object. GetOwnPropertyDescriptor, used to get the specified attributes describe objects, will replace the latter in the future.

var myObject = {}; Object.defineProperty(myObject, 'hidden', { value: true, enumerable: false, }); / / the old writing var theDescriptor = Object. GetOwnPropertyDescriptor (myObject, "hidden"); / / the new writing var theDescriptor = Reflect. GetOwnPropertyDescriptor (myObject, "hidden");Copy the code

Reflect. GetOwnPropertyDescriptor and Object. GetOwnPropertyDescriptor is a difference, if the first parameter is not Object, Object. GetOwnPropertyDescriptor (1, ‘foo’) is not an error, return to undefined, and Reflect. GetOwnPropertyDescriptor (1, ‘foo’) will throw an error, illegal said parameters.

Reflect.isExtensible (target)

The reflect. isExtensible method corresponds to object. isExtensible, which returns a Boolean value indicating whether the current Object isExtensible.

const myObject = {}; // Reflect. IsExtensible (myObject) // trueCopy the code

Object. IsExtensible returns false if the argument is not an Object, since non-objects are inherently unextensible, and Reflects. isExtensible returns an error.

Object. IsExtensible (1) // false Reflect. IsExtensible (1) // ErrorCopy the code

Reflect.preventExtensions(target)

Reflect. The corresponding Object preventExtensions. PreventExtensions method, used to make an Object into an extension. It returns a Boolean value indicating whether the operation succeeded.

var myObject = {}; / / the old writing Object. PreventExtensions (myObject) / / Object {} / / new writing Reflect. PreventExtensions (myObject) / / trueCopy the code

If the parameter is not Object, the Object preventExtensions error in ES5 environment, return to the incoming parameters on ES6 environment, and Reflect. PreventExtensions complains.

// ES5 environment Object.preventExtensions(1) // Error // ES6 environment Object.preventExtensions(1) // 1 // New Reflect. PreventExtensions (1) / / an errorCopy the code

Reflect.ownKeys (target)

Reflect. OwnKeys method is used to return all attributes of the Object, basic is equal to the Object. The getOwnPropertyNames and Object. The getOwnPropertySymbols combined.

var myObject = { foo: 1, bar: 2, [Symbol.for('baz')]: 3, [Symbol.for('bing')]: 4, }; / / the old writing Object. GetOwnPropertyNames (myObject) / / / 'foo', 'bar' Object. GetOwnPropertySymbols (myObject) / / [Symbol (baz), OwnKeys (myObject) // ['foo', 'bar', Symbol(baz), Symbol(bing)]Copy the code

An error is reported if the first argument to the reflect.ownkeys () method is not an object.

Example: Implement the observer pattern using Proxy

Observer mode refers to the function automatically observing the data object and executing it when the object changes.

Const person = Observable ({name: 'zhangsan ', age: 20}); function print() { console.log(`${person.name}, ${person.age}`) } observe(print); Person. Name = 'person '; // output // Li Si, 20Copy the code

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

Next, write the simplest implementation of the observer pattern using a Proxy, implementing the Observable and observe functions. The idea is that the Observable function returns a Proxy of the original object, intercepts assignment, and triggers functions that act as observers.

const queuedObservers = new Set();

const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, {set});

function set(target, key, value, receiver) {
  const result = Reflect.set(target, key, value, receiver);
  queuedObservers.forEach(observer => observer());
  return result;
}
Copy the code

In the above code, we define a Set into which all observer functions are placed. The Observable function then intercepts the assignment by returning the proxy of the original object. In the interceptor set, all observers are automatically executed.