Constr.apply (this, args) in ES6 classes

I used the following function to instantiate 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 use my library using native ES6 classes in node v4 +:

 class Person { constructor(name, surname) { this.name = name; this.surname = surname; } } var person = Kernel._construct(Person, ["name", surname]); // EXCEPTION! 

They get the error:

 TypeError: Class constructors cannot be invoked without 'new' 

I need to be able to call a constructor with an unknown number of arguments. Any ideas on how to get around this?

+5
source share
3 answers

There are various ways to do this:

  • Using Function methods:

     Kernel.prototype._construct = function (constr, args) { return new (Function.prototype.bind.apply(constr, [null].concat(args))); }; 

    Here we use args as arguments to bind . The goal is to have a function that can be called without side effects so that we can call new x() . bind does this for us, but we need to configure it correctly. Syntax:

     func.bind(thisArg[, arg1][, args2...]) // calling the results from the above is like // thisArg.func(arg1, arg2...); 

    We want to use constr as a function for binding, and elements in args as arguments. We don't care about thisArg . To do this, we need to "convert" the args array to arguments. The apply call does this:

     func.apply(thisArg[, args]); // calling the results from the above is like // thisArg.func(args[0], args[1]...); 

    apply actually calls bind . The first [null] element is important because we want to call bind , where thisArg is null - like this: constr.bind(null, args[0], args[1]...) .

  • Using ES2015 Distribution Operator :

     Kernel.prototype._construct = function (constr, args) { return new constr(...args); }; 

    This is much simpler, but there are two problems:

    • This requires ES2015 support, and even the latest Node (v4.2.1) requires a flag for this (--harmony_spreadcalls).
    • This will result in a syntax error if it is not supported, and you cannot even do it conditionally, in addition, using dynamic scripting ( eval() / new Function(...) ) is not recommended.
  • Using the Reflect built into the object.

     Kernel.prototype._construct = function (constr, args) { return Reflect.construct(constr, args); }; 

    It is also simple, but still lagging behind in terms of support (basically you should use babel).

+9
source

You can get around this by associating the arguments with the function and then calling it new:

 Kernel.prototype._construct = function (constr, args) { var F = constr.bind(null, args); return new F(); }; 
+2
source

Good question, but no , you cannot invoke constructors without the new keyword in ES6 in accordance with ECMAScript ยง9.22 .

I asked the same question: ES6: calling a constructor class without a new keyword

I need to be able to call a constructor with an unknown number of arguments. Any ideas on how to get around this?

Well this doesn't say anything about (not) using the new keyword, but you can easily use variable arguments in the ES6 constructor

 class Person { constructor(name, surname, ...others) { this.name = name; this.surname = surname; this.others = others || []; } } let p = new Person('OweR', 'ReLoaDeD', 'a', 'b', 'c'); p; //=> {"name":"OweR","surname":"ReLoaDeD","others":["a","b","c"]} 

I suspect that this is not what you need. But if you are trying to avoid using new to call a class in ES6, this is not possible.


So, with that said, what are you really trying to accomplish?

+2
source

Source: https://habr.com/ru/post/1233946/


All Articles