Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I call a javascript constructor using call or apply? [duplicate]

How could I generalise the function below to take N arguments? (Using call or apply?)

Is there a programmatic way to apply arguments to 'new'? I don't want the constructor to be treated like a plain function.

/**  * This higher level function takes a constructor and arguments  * and returns a function, which when called will return the   * lazily constructed value.  *   * All the arguments, except the first are pased to the constructor.  *   * @param {Function} constructor  */   function conthunktor(Constructor) {     var args = Array.prototype.slice.call(arguments, 1);     return function() {         console.log(args);         if (args.length === 0) {             return new Constructor();         }         if (args.length === 1) {             return new Constructor(args[0]);         }         if (args.length === 2) {             return new Constructor(args[0], args[1]);         }         if (args.length === 3) {             return new Constructor(args[0], args[1], args[2]);         }         throw("too many arguments");         } } 

qUnit test:

test("conthunktorTest", function() {     function MyConstructor(arg0, arg1) {         this.arg0 = arg0;         this.arg1 = arg1;     }     MyConstructor.prototype.toString = function() {         return this.arg0 + " " + this.arg1;     }      var thunk = conthunktor(MyConstructor, "hello", "world");     var my_object = thunk();     deepEqual(my_object.toString(), "hello world"); }); 
like image 469
fadedbee Avatar asked Jul 29 '10 12:07

fadedbee


People also ask

Can you call a method in a constructor JavaScript?

Yes, it is possible, when your constructor function executes, the this value has already the [[Prototype]] internal property pointing to the ValidateFields.

How do you call a constructor a second time?

It's not possible to call the constructor two times without using reflection as it's called only once to construct the object. Instance constructors are used to create and initialize any instance member variables when you use the new expression to create an object of a class.

Can a JavaScript class have multiple constructors?

JavaScript doesn't have function overloading, including for methods or constructors. If you want a function to behave differently depending on the number and types of parameters you pass to it, you'll have to sniff them manually.


1 Answers

This is how you do it:

function applyToConstructor(constructor, argArray) {     var args = [null].concat(argArray);     var factoryFunction = constructor.bind.apply(constructor, args);     return new factoryFunction(); }  var d = applyToConstructor(Date, [2008, 10, 8, 00, 16, 34, 254]); 

Call is slightly easier

function callConstructor(constructor) {     var factoryFunction = constructor.bind.apply(constructor, arguments);     return new factoryFunction(); }  var d = callConstructor(Date, 2008, 10, 8, 00, 16, 34, 254); 

You can use either of these to create factory functions:

var dateFactory = applyToConstructor.bind(null, Date) var d = dateFactory([2008, 10, 8, 00, 16, 34, 254]); 

or

var dateFactory = callConstructor.bind(null, Date) var d = dateFactory(2008, 10, 8, 00, 16, 34, 254); 

It will work with any constructor, not just built-ins or constructors that can double as functions (like Date).

However it does require the Ecmascript 5 .bind function. Shims will probably not work correctly.

A different approach, more in the style of some of the other answers is to create a function version of the built in new. This will not work on all builtins (like Date).

function neu(constructor) {     // http://www.ecma-international.org/ecma-262/5.1/#sec-13.2.2     var instance = Object.create(constructor.prototype);     var result = constructor.apply(instance, Array.prototype.slice.call(arguments, 1));      // The ECMAScript language types are Undefined, Null, Boolean, String, Number, and Object.     return (result !== null && typeof result === 'object') ? result : instance; }  function Person(first, last) {this.first = first;this.last = last}; Person.prototype.hi = function(){console.log(this.first, this.last);};  var p = neu(Person, "Neo", "Anderson"); 

And now, of course you can do .apply or .call or .bind on neu as normal.

For example:

var personFactory = neu.bind(null, Person); var d = personFactory("Harry", "Potter"); 

I feel that the first solution I give is better though, as it doesn't depend on you correctly replicating the semantics of a builtin and it works correctly with builtins.

like image 73
kybernetikos Avatar answered Oct 09 '22 08:10

kybernetikos