I have used the following function to create instances of unknown classes for some time:
Kernel.prototype._construct = function (constr, args) {
function F() {
constr.apply(this, args); // EXCEPTION!
}
F.prototype = constr.prototype;
return new F();
};
If I use prototypes everything works:
function Person(name, surname) {
this.name = name;
this.surname = surname;
}
var person = Kernel._construct(Person, ["name", "surname"]); // WORKS!
However, some people are using my library using ES6 native classes in node v4+:
class Person {
constructor(name, surname) {
this.name = name;
this.surname = surname;
}
}
var person = Kernel._construct(Person, ["name", surname]); // EXCEPTION!
They are getting an error:
TypeError: Class constructors cannot be invoked without 'new'
I need to be able to invoke the constructor with an unknown number of arguments. Any ideas about how to get around this issue?
Prior to ES6, creating a class was a fussy affair. Classes can be created using the class keyword in ES6. Classes can be included in the code either by declaring them or by using class expressions. The class keyword is followed by the class name.
ES6 allows function parameters to have default values. The rest parameter (...) allows a function to treat an indefinite number of arguments as an array: The includes () method returns true if a string contains a specified value, otherwise false: let text = "Hello world, welcome to the universe.";
ES6 enables a child class to invoke its parent class data member. This is achieved by using the super keyword. The super keyword is used to refer to the immediate parent of a class. The doPrint () redefinition in the class StringWriter, issues a call to its parent class version.
expression − Starting with ES6, you can also use expressions as a property name to bind to the given function. The above example defines a class Student with three properties namely rno, fname and lname. The getter function fullName () concatenates the fname and lname and returns a new string.
There are various ways you can do that:
Using Function
object's methods:
Kernel.prototype._construct = function (constr, args) {
return new (Function.prototype.bind.apply(constr, [null].concat(args)));
};
Here we're applying args
as arguments for bind
. The goal is to have a function that can be called without arugments so that we can call new x()
. bind
does this for us, but we need to set it up correctly. The syntax is:
func.bind(thisArg[, arg1][, args2...])
// calling the results from the above is like
// thisArg.func(arg1, arg2...);
We want to use constr
as the function to bind, and the items in args
as the arguments. We don't care about thisArg
. To do that, we need to "convert" the args
array to arguments. The apply
call does that:
func.apply(thisArg[, args]);
// calling the results from the above is like
// thisArg.func(args[0], args[1]...);
apply
is actually calling bind
. The first item,[null]
, is important because we want to call bind
where thisArg
is null
- like this: constr.bind(null, args[0], args[1]...)
.
Using ES2015 Spread operator:
Kernel.prototype._construct = function (constr, args) {
return new constr(...args);
};
This is much simpler, but there are 2 problems:
eval()
/ new Function(...)
) - which is not advised.Using the Reflect
built-in in object.
Kernel.prototype._construct = function (constr, args) {
return Reflect.construct(constr, args);
};
This is also simple, but is even further behind in terms of support (basically you must use babel).
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