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!
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!
- I don't think it's about enabling things that haven't not been possible before, but rather providing a unified API for these kind if "meta" operations. – Felix Kling Commented Jan 6, 2017 at 22:14
-
@FelixKling I suppose APIs as shortcuts for lengthier ways of doing something are valid, but I suppose I'm also curious to know if
Reflect.construct
solves any problems aside from possibly being a shortcut. – trusktr Commented Jan 6, 2017 at 22:40 - @FelixKling I updated my answer, I think I might have found a use case. – trusktr Commented Jan 6, 2017 at 22:41
-
1
"The cool thing about it is that this is as expected inside the prototype methods!" It would be anyway, thanks to the way you're calling them. All that using
Reflect.construct
does in the above is ensure thatnew.target
is set for the calls toBar
andFoo
(which could indeed be useful; that's basically a generalized version of my #3). – T.J. Crowder Commented May 13, 2020 at 7:44 -
Subclass
Error
, for example. – dumbass Commented May 10, 2024 at 5:28
4 Answers
Reset to default 5The 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 useslength
and directly accesses the0
,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 ofError
orArray
or a web ponent (some people don't like to useclass
, 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"
It's late on the topic, but it is in fact possible to properly subclass native classes in ES5 code. It looks like this:
//ES6-class version
class MyArray extends Array {
peekLast() {
return this[this.length - 1];
}
}
//ES5-class version
function MyArray() {
//No need to use new since that instance gets discarded
var retval = new Array();
retval.__proto__ = MyArray.prototype; //This is the key line
//Do any other initialization to retval here.
return retval;
}
MyArray.prototype = Object.create(Array.prototype, {
constructor: {
configurable: true,
writable: true,
value: MyArray
},
peekLast: {
configurable: true,
writable: true,
value: function peekLast() { return this[this.length - 1]; }
}
});
MyArray.__proto__ = Array;
I get that __proto__
was never in the ES standard before ES6, but it was a defacto web standard that all major browsers and engines supported. That's what makes it possible.
It accepts the constructor whose prototype should be used in Reflect.constructor 3rd parameter. Try this code below and take a look to the prototype in array1Proto. It set Array as constructor despite was Object/func1 at the beginning.
function func1(a, b, c) {
this.sum = a + b + c;
}
const args = [1, 2, 3];
const object1 = new func1(...args);
const object2 = Reflect.construct(func1, args);
const array1Proto = Reflect.construct(func1, args, Array);
console.log(object1)
console.log(object2)
console.log(array1Proto)