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:
var myObject = 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
:
var s = Symbol('foo');
var k = 'bar';
var o = { [s]: 1, [k]: 1 };
// Simulate [[OwnPropertyKeys]]
var keys = Object.getOwnPropertyNames(o).concat(Object.getOwnPropertySymbols(o));
assert.deepEqual(keys, [k, s]);
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. CallingObject.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
, anddelete
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:
var ages = [11, 33, 12, 54, 18, 96];
// Function.prototype style:
var youngest = Math.min.apply(Math, ages);
var oldest = Math.max.apply(Math, ages);
var type = Object.prototype.toString.call(youngest);
// Reflect style:
var youngest = Reflect.apply(Math.min, Math, ages);
var oldest = Reflect.apply(Math.max, Math, ages);
var type = Reflect.apply(Object.prototype.toString, youngest);
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:
function totalNumbers() {
return Array.prototype.reduce.call(arguments, function (total, next) {
return total + next;
}, 0);
}
totalNumbers.apply = function () {
throw new Error('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;
Reflect.construct ( target, argumentsList [, constructorToCreateThis] )
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:
class Greeting {
constructor(name) {
this.name = name;
}
greet() {
return `Hello ${name}`;
}
}
// ES5 style factory:
function greetingFactory(name) {
var instance = Object.create(Greeting.prototype);
Greeting.call(instance, name);
return instance;
}
// ES6 style factory
function greetingFactory(name) {
return Reflect.construct(Greeting, [name], Greeting);
}
// Or, omit the third argument, and it will default to the first argument.
function greetingFactory(name) {
return Reflect.construct(Greeting, [name]);
}
// Super slick ES6 one liner factory function!
const greetingFactory = (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 target
s, 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.
function MyDate() {
/*…*/
}
// 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
.
var myObject = {};
Object.defineProperty(myObject, 'hidden', {
value: true,
enumerable: false,
});
var theDescriptor = Reflect.getOwnPropertyDescriptor(myObject, 'hidden');
assert.deepEqual(theDescriptor, { value: true, enumerable: true });
// Old style
var theDescriptor = Object.getOwnPropertyDescriptor(myObject, 'hidden');
assert.deepEqual(theDescriptor, { value: true, enumerable: true });
assert(Object.getOwnPropertyDescriptor(1, 'foo') === undefined)
Reflect.getOwnPropertyDescriptor(1, 'foo'); // throws TypeError
Reflect.deleteProperty ( target, propertyKey )
Reflect.deleteProperty
will, surprise surprise, delete a property off of the target object. Pre ES6, you'd typically write delete obj.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:
var myObj = { foo: 'bar' };
delete myObj.foo;
assert(myObj.hasOwnProperty('foo') === false);
myObj = { foo: 'bar' };
Reflect.deleteProperty(myObj, 'foo');
assert(myObj.hasOwnProperty('foo') === false);
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.
var myObj = new FancyThing();
assert(Reflect.getPrototypeOf(myObj) === FancyThing.prototype);
// Old style
assert(Object.getPrototypeOf(myObj) === FancyThing.prototype);
Object.getPrototypeOf(1); // undefined
Reflect.getPrototypeOf(1); // TypeError
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 TypeError
s from passing in incorrect arguments.
var myObj = new FancyThing();
assert(Reflect.setPrototypeOf(myObj, OtherThing.prototype) === true);
assert(Reflect.getPrototypeOf(myObj) === OtherThing.prototype);
// Old style
assert(Object.setPrototypeOf(myObj, OtherThing.prototype) === myObj);
assert(Object.getPrototypeOf(myObj) === FancyThing.prototype);
Object.setPrototypeOf(1); // TypeError
Reflect.setPrototypeOf(1); // TypeError
var myFrozenObj = new FancyThing();
Object.freeze(myFrozenObj);
Object.setPrototypeOf(myFrozenObj); // TypeError
assert(Reflect.setPrototypeOf(myFrozenObj) === false);
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 (typeof target !== '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:
var myObject = {};
var myNonExtensibleObject = Object.preventExtensions({});
assert(Reflect.isExtensible(myObject) === true);
assert(Reflect.isExtensible(myNonExtensibleObject) === false);
Reflect.isExtensible(1); // throws TypeError
Reflect.isExtensible(false); // throws TypeError
// Using Object.isExtensible
assert(Object.isExtensible(myObject) === true);
assert(Object.isExtensible(myNonExtensibleObject) === false);
// ES5 Object.isExtensible semantics
Object.isExtensible(1); // throws TypeError on older browsers
Object.isExtensible(false); // throws TypeError on older browsers
// ES6 Object.isExtensible semantics
assert(Object.isExtensible(1) === false); // only on newer browsers
assert(Object.isExtensible(false) === false); // only on newer browsers
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.
var myObject = {};
var myObjectWhichCantPreventExtensions = magicalVoodooProxyCode({});
assert(Reflect.preventExtensions(myObject) === true);
assert(Reflect.preventExtensions(myObjectWhichCantPreventExtensions) === false);
Reflect.preventExtensions(1); // throws TypeError
Reflect.preventExtensions(false); // throws TypeError
// Using Object.isExtensible
assert(Object.isExtensible(myObject) === true);
Object.isExtensible(myObjectWhichCantPreventExtensions); // throws TypeError
// ES5 Object.isExtensible semantics
Object.isExtensible(1); // throws TypeError
Object.isExtensible(false); // throws TypeError
// ES6 Object.isExtensible semantics
assert(Object.isExtensible(1) === false);
assert(Object.isExtensible(false) === false);
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:
var myArray = [1, 2, 3];
myArray[Symbol.enumerate] = function () {
throw new Error('Nope!');
}
for (let item of myArray) { // error thrown: Nope!
}
for (let item of Reflect.enumerate(myArray)) {
// 1 then 2 then 3
}
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:
var myObject = {
foo: 1,
bar: 2,
get baz() {
return this.foo + this.bar;
},
}
assert(Reflect.get(myObject, 'foo') === 1);
assert(Reflect.get(myObject, 'bar') === 2);
assert(Reflect.get(myObject, 'baz') === 3);
assert(Reflect.get(myObject, 'baz', myObject) === 3);
var myReceiverObject = {
foo: 4,
bar: 4,
};
assert(Reflect.get(myObject, 'baz', myReceiverObject) === 8);
// Non-objects throw:
Reflect.get(1, 'foo'); // throws TypeError
Reflect.get(false, 'foo'); // throws TypeError
// These old styles don't throw:
assert(1['foo'] === undefined);
assert(false['foo'] === undefined);
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:
var myObject = {
foo: 1,
set bar(value) {
return this.foo = value;
},
}
assert(myObject.foo === 1);
assert(Reflect.set(myObject, 'foo', 2));
assert(myObject.foo === 2);
assert(Reflect.set(myObject, 'bar', 3));
assert(myObject.foo === 3);
assert(Reflect.set(myObject, 'bar', myObject) === 4);
assert(myObject.foo === 4);
var myReceiverObject = {
foo: 0,
};
assert(Reflect.set(myObject, 'bar', 1, myReceiverObject));
assert(myObject.foo === 4);
assert(myReceiverObject.foo === 1);
// Non-objects throw:
Reflect.set(1, 'foo', {}); // throws TypeError
Reflect.set(false, 'foo', {}); // throws TypeError
// These old styles don't throw:
1['foo'] = {};
false['foo'] = {};
assert(1['foo'] === undefined);
assert(false['foo'] === undefined);
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, {
get bar() {
return 2;
},
baz: 3,
});
// Without Reflect.has
assert(('foo' in myObject) === true);
assert(('bar' in myObject) === true);
assert(('baz' in myObject) === true);
assert(('bing' in myObject) === 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:
var myObject = {
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:
var keys = 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.