Metaprogramming in ES6: Part 2 - Reflect

In my last post we had a look at Symbols, and how they add useful new metaprogramming features to JavaScript. This time, we’re (finally!) going to talk all about Reflect. If you haven’t read Part 1: Symbols, then I’d recommend you do. In the last post, I made a key point which I’m going to reiterate:

Symbols are all about Reflection within implementation - you sprinkle them on your existing classes and objects to change the behaviour.

Reflect is all about Reflection through introspection - used to discover very low level information about your code.

Proxy is all about Reflection through intercession - wrapping objects and intercepting their behaviours through traps.

Reflect is a new global Object (like JSON or Math) that provides a bunch of useful introspection methods (introspection is really just a fancy word for “looking at stuff”). Introspection tools already exist in JavaScript; Object.keys, Object.getOwnPropertyNames, etc. So why the need for a new API when these could just be added to Object?

“Internal Methods”

All JavaScript specs, and therefore engines, come with a series of “internal methods”. Effectively these let the JavaScript engine perform essential operations on your Objects as it hops around your code. If you read through the spec, you’ll find these everywhere, things like [[Get]], [[Set]], [[HasOwnProperty]] and so on (if you’re having trouble sleeping, the full list of internal methods in ES5 Section 8.12/ES6 Section 9.1).

Some of these “internal methods” were hidden from JavaScript code, others were applied in part by some methods, and even if there were available, they were tucked away inside various crevices, for example Object.prototype.hasOwnProperty is an implementation of [[HasOwnProperty]], except not every object inherits from Object, and so you have to perform convoluted incantations just to use it - for example:

varmyObject=Object.create(null);// Happens more often than you might think (especially with new ES6 classes)assert(myObject.hasOwnProperty===undefined);// If you want to use hasOwnProperty on `myObject`:Object.prototype.hasOwnProperty.call(myObject,'foo');

As another example, the [[OwnPropertyKeys]] internal method gets all of an Objects String keys and Symbol keys as one Array. The only way to get these (outside of Reflect) is to combined the results of Object.getOwnPropertyNames and Object.getOwnPropertySymbols:

Reflect methods

Reflect is effectively a collection of all of those “internal methods” that were available exclusively through the JavaScript engine internals, now exposed in one single, handy object. You might be thinking “yeah, but why not just attach these to Object like Object.keys, Object.getOwnPropertyNames etc are?”. Here is why:

Reflect has methods that are not meant just for Objects, for example Reflect.apply - which targets a Function. Calling Object.apply(myFunction) would just look weird.

Having a single object to house these methods is a great way to keep the rest of JavaScript clean, rather than dotting Reflection methods throughout constructors and prototypes - or worse - globals.

typeof, instanceof, and delete already exist as Reflection operators - adding new keywords like this is not only cumbersome for developers, but also a nightmare for backwards compatibility and explodes the number of reserved words.

Reflect.apply ( target, thisArgument [, argumentsList] )

Reflect.apply is pretty much just Function#apply - it takes a function, and calls it with a context, and an array of arguments. From this point on you could consider the Function#call/Function#apply versions deprecated. This isn’t mind blowing, but it makes good sense. Here’s how you use it:

The real benefit of Reflect.apply over Function.prototype.apply is defensibility: any code could trivially change the functions call or apply method, leaving you stuck with broken code or horrible workarounds. This doesn’t really end up being a huge deal in the real world, but code like the following could certainly exist:

functiontotalNumbers(){returnArray.prototype.reduce.call(arguments,function(total,next){returntotal+next;},0);}totalNumbers.apply=function(){thrownewError('Aha got you!');}totalNumbers.apply(null,[1,2,3,4]);// throws Error('Aha got you!');// The only way to defensively do this in ES5 code is horrible:Function.prototype.apply.call(totalNumbers,null,[1,2,3,4])===10;//You could also do this, which is still not much cleaner:Function.apply.call(totalNumbers,null,[1,2,3,4])===10;// Reflect.apply to the rescue!Reflect.apply(totalNumbers,null,[1,2,3,4])===10;

Similar to Reflect.apply - this lets you call a Constructor with a set of arguments. This will work with Classes, and sets up the correct object so that Constructors have the right this object with the matching prototype. In ES5 land, you’d use the Object.create(Constructor.prototype) pattern, and pass that to Constructor.call or Constructor.apply. The difference with Reflect.construct is that rather than passing an object, you just pass the constructor - and Reflect.construct will handle all that jazz (alternatively, just omit it and it’ll default to the target argument). The old style of doing this was quite cumbersome, the new style can be much more succinct, as little as a one liner:

classGreeting{constructor(name){this.name=name;}greet(){return`Hello${name}`;}}// ES5 style factory:functiongreetingFactory(name){varinstance=Object.create(Greeting.prototype);Greeting.call(instance,name);returninstance;}// ES6 style factoryfunctiongreetingFactory(name){returnReflect.construct(Greeting,[name],Greeting);}// Or, omit the third argument, and it will default to the first argument.functiongreetingFactory(name){returnReflect.construct(Greeting,[name]);}// Super slick ES6 one liner factory function!constgreetingFactory=(name)=>Reflect.construct(Greeting,[name]);

Reflect.defineProperty ( target, propertyKey, attributes )

Reflect.defineProperty pretty much takes over from Object.defineProperty - it lets you define metadata about a property. It fits much better here because Object.* implies that it acts on object literals (it is, after all, the Object literal constructor), while Reflect.defineProperty just implies that what you’re doing is Reflection, which is more semantic.

An important note is that Reflect.defineProperty - just like Object.defineProperty - will throw a TypeError for invalid targets, such Number or String primitives ( Reflect.defineProperty(1,'foo')). This is a good thing, because throwing errors for wrong argument types notifies you of problems much better than silently failing.

Once again, you could consider Object.defineProperty pretty much deprecated from here on out. Use Reflect.defineProperty instead.

functionMyDate(){/*…*/}// Old Style, weird because we're using Object.defineProperty to define// a property on Function (why isn't there a Function.defineProperty?)Object.defineProperty(MyDate,'now',{value:()=>currentms});// New Style, not weird because Reflect does Reflection.Reflect.defineProperty(MyDate,'now',{value:()=>currentms});

Reflect.getOwnPropertyDescriptor ( target, propertyKey )

This, once again, pretty much replaces Object.getOwnPropertyDescriptor, getting the descriptor metadata of a property. The key difference is that while Object.getOwnPropertyDescriptor(1,'foo') silently fails, returning undefined, Reflect.getOwnPropertyDescriptor(1,'foo') will throw a TypeError - it throws for invalid arguments, just like Reflect.defineProperty does. You’re probably getting the idea by now - but Reflect.getOwnPropertyDescriptor pretty much deprecates Object.getOwnPropertyDescriptor.

Reflect.deleteProperty ( target, propertyKey )

Reflect.deleteProperty will, surprise surprise, delete a property off of the target object. Pre ES6, you’d typically write deleteobj.foo, now you can write Reflect.deleteProperty(obj,'foo'). This is slightly more verbose, and the semantics are slightly different to the delete keyword, but it has the same basic effect for objects. Both of them call the internal target[[Delete]](propertyKey) method - but the delete operator also “works” for non-object references (i.e. variables), and so it does more checking on the operand passed to it, and has more potential to throw:

Once again, you could consider this to be the “new way” to delete properties - if you wanted to. It’s certainly more explicit to its intention.

Reflect.getPrototypeOf ( target )

The theme of replacing/deprecating Object methods continues - this time Object.getPrototypeOf. Just like its siblings, the new Reflect.getPrototypeOf method will throw a TypeError if you give it an invalid target such as a Number or String literal, null or undefined, where Object.getPrototypeOf coerces the target to be an object - so 'a' becomes Object('a'). Syntax otherwise, is exactly the same.

Reflect.setPrototypeOf ( target, proto )

Of course, you couldn’t have getPrototypeOf without setPrototypeOf. Now, Object.setPrototypeOf will throw for non-objects, but it tries to coerce the given argument into an Object, and also if the [[SetPrototype]] internal operation fails, it’ll throw a TypeError, if it succeeds it’ll return the target argument. Reflect.setPrototypeOf is much more basic - if it receives a non-object it’ll throw a TypeError, but other than that, it’ll just return the result of [[SetPrototypeOf]] - which is a Boolean indicating if the operation was successful. This is useful because then you can manage
the outcome without resorting to using a try/catch which will also catch any other TypeErrors from passing in incorrect arguments.

Reflect.isExtensible (target)

Ok, once again this one is just a replacement of Object.isExtensible - but its a bit more complicated than that. Prior to ES6 (so… ES5) Object.isExtensible threw a TypeError if you fed it a non-object (typeoftarget!=='object'). ES6 semantics have changed this (Gasp! A change to the existing API!) so that passing in a non-object to Object.isExtensible will now return false - because non-objects are all not extensible. So code like Object.isExtensible(1)===false would throw, whereas ES6 runs the statement like you’d expect (evaluating to true).

The point of the brief history lesson is that Reflect.isExtensible uses the old behavior, of throwing on non-objects. I’m not really sure why it does, but it does. So technically Reflect.isExtensible changes the semantics against Object.isExtensible, but Object.isExtensible changed anyway. Here’s some code to illustrate:

Reflect.preventExtensions ( target )

This is the last method in the Reflection object that borrows from Object. This follows the same story as Reflect.isExtensible; ES5’s Object.preventExtensions used to throw on non-objects, but now in ES6 it returns the value back, while Reflect.preventExtensions follows the old ES5 behaviour - throwing on non-objects. Also, while Object.preventExtensions has the potential to throw, Reflect.preventExtensions will simply return true or false, depending on the success of the operation, allowing you to gracefully handle the failure scenario.

Reflect.enumerate ( target )

Update: This was removed in ES2016 (aka ES7). myObject[Symbol.iterator]() is the only way to enumerate an Object’s keys or values now.

Finally a completely new Reflect method! Reflect.enumerate uses the same semantics as the new Symbol.iterator function (discussed in the previous article), both use the hidden [[Enumerate]] method that JavaScript engines are aware of. In other words, the only alternative to Reflect.enumerate is myObject[Symbol.iterator](), except of course the Symbol.iterator can be overridden, while Reflect.enumerate can never be overridden. Used like so:

Reflect.get ( target, propertyKey [ , receiver ])

Reflect.get is also a completely new method. It’s quite a simple method; it effectively calls target[propertyKey]. If target is a non-object, the function call will throw - which is good because currently if you were to do something like 1['foo'] it just silently returns undefined, while Reflect.get(1,'foo') will throw a TypeError! One interesting part of Reflect.get is the receiver argument, which essentially acts as the this argument if target[propertyKey] is a getter function, for example:

Reflect.set ( target, propertyKey, V [ , receiver ] )

You can probably guess what this method does. It’s the sibling to Reflect.get, and it takes one extra argument - the value to set. Just like Reflect.get, Reflect.set will throw on non-objects, and has a special receiver argument which acts as the this value if target[propertyKey] is a setter function. Obligatory code example:

Reflect.has ( target, propertyKey )

Reflect.has is an interesting one, because it is essentially the same functionality
as the in operator (outside of a loop). Both use the [[HasProperty]] internal method, and both throw if the target isn’t an object. Because of this there’s little point in using Reflect.has over in unless you prefer the function-call style, but it has important use in other parts of the language, which will become clear in the next post. Anyway, here’s how you use it:

myObject={foo:1,};Object.setPrototypeOf(myObject,{getbar(){return2;},baz:3,});// Without Reflect.hasassert(('foo'inmyObject)===true);assert(('bar'inmyObject)===true);assert(('baz'inmyObject)===true);assert(('bing'inmyObject)===false);// With Reflect.has:assert(Reflect.has(myObject,'foo')===true);assert(Reflect.has(myObject,'bar')===true);assert(Reflect.has(myObject,'baz')===true);assert(Reflect.has(myObject,'bing')===false);

Reflect.ownKeys ( target )

This has already been discussed a tiny bit in this article, you see Reflect.ownKeys implements [[OwnPropertyKeys]] which if you recall above is a combination of Object.getOwnPropertyNames and Object.getOwnPropertySymbols. This makes Reflect.ownKeys uniquely useful. Lets see shall we:

varmyObject={foo:1,bar:2,[Symbol.for('baz')]:3,[Symbol.for('bing')]:4,};assert.deepEqual(Object.getOwnPropertyNames(myObject),['foo','bar']);assert.deepEqual(Object.getOwnPropertySymbols(myObject),[Symbol.for('baz'),Symbol.for('bing')]);// Without Reflect.ownKeys:varkeys=Object.getOwnPropertyNames(myObject).concat(Object.getOwnPropertySymbols(myObject));assert.deepEqual(keys,['foo','bar',Symbol.for('baz'),Symbol.for('bing')]);// With Reflect.ownKeys:assert.deepEqual(Reflect.ownKeys(myObject),['foo','bar',Symbol.for('baz'),Symbol.for('bing')]);

Conclusion

We’ve pretty exhaustively gone over every Reflect method. We’ve seen some are newer versions of common existing methods, sometimes with a few tweaks, and some are entirely new methods - allowing new levels of Reflection within JavaScript. If you want to - you could totally ditch Object.*/Function.* methods and use the new Reflect ones instead, if you don’t want to - don’t sweat it, nothing bad will happen.

Now, I don’t want you to go away empty handed. If you want to use Reflect, then I’ve got your back - as part of the work behind this post, I submitted a pull request to eslint and as of v1.0.0, ESlint has a prefer-reflect rule which you can use to get ESLint to tell you off when you use the older version of Reflect methods. You could also take a look at my eslint-config-strict config, which has the prefer-reflect turned on (plus a bunch of others). Of course, if you decide you want to use Reflect, you’ll probably need to polyfill it; luckily there’s some good polyfills out there, such as core-js and harmony-reflect.

What do you think about the new Reflect API? Plan on using it in your project? Let me know, in the comments below or on Twitter, where I’m @keithamus.

Oh - also don’t forget, the third and final part of this series - Part 3 Proxies - will be out soon, and I’ll try not to take 2 months to release it again!

Lastly, thanks to @mttshw and @WebReflection for scrutinising my work and making this post much better than it would have been.