The Reflect API of ES6


Reflection in a programming language is the act of inspecting, dynamically calling, and modifying classes, objects, properties, and methods. In other words, reflection is the ability of the programming language to reflect on the structure of the code.

The ES6 Reflect API gives you a Reflect object that lets you call methods, construct objects, getting and setting prototypes, manipulating and extending properties.

Reflection is important, because it lets you write programs and frameworks that are able to handle a non-static code structure.

One use case is automated testing. You don’t have to do expensive and complicated setup operations in order to test some methods of a class in isolation.

We can also use the Reflect API in ES6 proxies.

Calling functions and methods: Reflect.apply

Reflect.apply calls functions or methods.

Creating objects: Reflect.construct

We can instantiate classes with Reflect.construct.

Arguments of Reflect.construct:

  • target: the function we will construct
  • args: array of arguments
  • newTarget: new.target value (optional)

With the third example, we have full control over the value of new.target in the constructor:

Manipulating prototypes

We can get the prototype of any object using the Reflect API.

As you can see from the example, this prototype is the same as the prototype of our PrivateAccount class.

We can also set prototypes using Reflect.setPrototypeOf:

In the above example, we set the prototype of myAccount to newProto, and define a contact getter function. After changing the prototype, the new getter method is executed.

Property access and modification

Reflect.has determines if a property exists for a given target object. The call enumerates all properties, not only own properties.

Reflect.ownKeys returns all own properties of a target in an array.

Reflect.get gets a property based on a key. As an optional parameter, the this context can be specified.

Getting the name property of myAccount is straightforward. In the second example, getting the contact property requires the execution of a getter method. As we redefined this getter method using Reflect.setPrototypeOf not too long ago, the result becomes "Zsolt - 555-1269".

If we specify the context as the third argument of Reflect.get, we will get another result for the contact property:

We can also set a property of our target using Reflect.set.

The fourth argument of Reflect.set is an optional context. Solve Exercise 1 at the end of the article to figure out how the context works in Reflect.set.

Reflect.defineProperty defines a new property. It is similar to calling Object.defineProperty. The difference between the two calls is that Reflect.defineProperty returns a boolean, while Object.defineProperty returns the object itself.

In the following example, we will define a writable property:

For a complete list of flags and their default values, check out the documentation of Object.defineProperty. Notice that all configuration flags inside the attributes object have a default value of false. This means that without specifying the writable flag, target.response would have been a read-only property.

We have accessed, created, and updated properties. The last operation missing is the deletion of properties.

Exercise 2 is about Reflect.deleteProperty and Reflect.defineProperty. You will find out that in some circumstances, deleting a property is not possible.

It is possible to prevent property extensions by calling the preventExtensions method of the Reflect API:

As you can see, the score field is not added to the test object, as test was locked down by Reflect.preventExtensions.

Reflect.isExtensible tests whether an object is extensible:

Exercises

These are two exercises from the book ES6 in Practice. The section on the Reflect API and Proxies will be added to the book on December 30th.

Exercise 1: Suppose a Person class is given.

Let’s create a person object and a newContext variable.

If we query the contents of person, we can see how the setter transformed the name into a first and a last field.

Let’s call a Reflect.set operation, setting the name field of our person object, and let’s add the new context in the fourth variable.

Determine the following values without executing the code:

  • the return value of the above Reflect.set call
  • person.first and person.last
  • person.name
  • newContext.first and newContext.last
  • newContext.name

Solution:

  • Reflect.set returns true whenever the set operation succeeds
  • As the first and the last members were not changed by the Reflect.set operation, person.first stays 'Julius', and person.last stays 'Caesar'. We are observing the operation of the setter method of person on a fresh context, which has nothing to do with the state stored in the person object. Notice how cool this feature is from the perspective of testing
  • person.name returns undefined, as no getter method was defined in the Person class
  • newContext.first becomes 'Alexander', and newContext.last becomes 'Severus', because the setter method of person was executed on newContext, creating or updating the first and last fields
  • newContext.name stays 'Marcus Aurelius', as the code didn’t change the value of the name property anywhere

Exercise 2: Recall the following code:

Let’s try to delete target.response.

The return value of the deleteProperty call indicates that the deletion is unsuccessful. Why? How can we modify the code such that the same Reflect.deleteProperty call returns true, and target.response is deleted?

Solution: The problem is that the response object is not configurable.

Read the documentation referenced in the lesson. Setting the configurable flag to true is necessary and sufficient for allowing the deletion of the defined property:

Would you like to learn ES6?
Strengthen your JavaScript knowledge with marketable skills!
Get the Course "ES6 in Practice"!

Learn Marketable Skills.

Verify your knowledge with real world exercises.

Thank you for your subscription.
Please check your inbox to access the first lesson.
  • WOW, why haven’t I heard of this yet?
    Thanks for sharing, awesome stuff šŸš€