I'm trying to find a good reason to use Reflect.construct
to achieve something noteworthy that I could not achieve before.
I'm not looking for an answer like the one here, because that example don't seem to be very useful. For example, why would I write
function greetingFactory(name) {
return Reflect.construct(Greeting, [name]);
}
when I can just write
function greetingFactory(name) {
return new Greeting(name);
}
?
Do you know any notable use cases for Reflect.construct
?
EDIT: Seems like I may have found a use case myself, but I'm not sure if it is solid and if it won't fall apart, but basically it seems like I can make new.target
work with ES5-style classes by writing them like this:
function Foo() {
console.log('Foo, new.target:', new.target)
this.name = "foo"
}
Foo.prototype.sayHello = function sayHello() {
return this.name
}
function Bar() {
console.log('Bar, new.target:', new.target)
let _ = Reflect.construct(Foo, [], new.target)
_.name = _.name + " bar"
return _
}
Bar.prototype = Object.create(Foo.prototype)
Bar.prototype.sayHello = function() {
return "Hello " + Foo.prototype.sayHello.call(this) + "!"
}
function Baz() {
console.log('Baz, new.target:', new.target)
let _ = Reflect.construct(Bar, [], new.target)
_.name = _.name + " baz"
return _
}
Baz.prototype = Object.create(Bar.prototype)
Baz.prototype.sayHello = function() {
return Bar.prototype.sayHello.call(this) + " Hello again!"
}
let baz = new Baz
console.log(baz.sayHello())
The cool thing about it is that this
is as expected inside the prototype methods!
The only three use cases I know of for Reflect.construct
are:
Using it within a Proxy construct
trap to get the default behavior (or to get slightly-modified default behavior).
Using it to avoid creating and using an iterator when you need to call a constructor function using an array whose elements need to be passed as discrete arguments. You can just do
t = new Thing(...theArray);
but that involves creating and using an iterator, whereas
t = Reflect.construct(Thing, theArray);
doesn't use an iterator, which is much less work (not that it usually matters; this is for a situation where you know time is crucial). (Instead of an iterator, construct
just uses length
and directly accesses the 0
, 1
, etc. properties.)
Neither of those options was available before ES2015. Instead, you had to do this:
t = Thing.apply(Object.create(Thing.prototype), theArray);
which worked with ES5 constructor functions. (It wouldn't work with an ES2015+ constructor function created via class
, but you don't need it to — you'd use one of the two options above instead.)
Using it to avoid using class
when constructing an instance of a subtype of Error
or Array
or a web component (some people don't like to use class
, and there are good arguments for that in projects that may need to be transpiled). (That said, Reflect.construct
can't be perfectly polyfilled, either.)
I've been trying to sort out a useful application of the Reflect.construct
as well. I think I may have found something but it would be nice to bounce the idea off other people on a similar path.
What I was thinking is that you could use Reflect.construct
to wedge a [[Prototype]]
between the instantiated object and it's intended [[Prototype]]
. This would allow you to shadow properties and methods that belong to the intended [[Prototype]]
without being too intrusive.
function Wedge() {};
Wedge.prototype = Object.create(String.prototype);
Wedge.prototype.toUpperCase = function () {
return "nope";
}
let someString = Reflect.construct(String, ['Foo Bar'], Wedge)
someString.toUpperCase() // "nope"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With