In JavaScript, I want to create an object instance (via the new operator), but pass any number of parameters to the constructor.Is this possible?
What I want to do is (but the code below doesn't work):
function Something(){ // init stuff } function createSomething(){ return new Something.apply(null, arguments); } var s = createSomething(a,b,c); // 's' is an instance of Something
Answer
As you can see from the response here, the.apply() built-in method was not invoked using the new operator.However, some very interesting solutions have been suggested.
My preferred solution is Matthew Crumley's Solution (I have modified it to pass the arguments property):
var createSomething = (function() { function F(args) { return Something.apply(this, args); } F.prototype = Something.prototype; return function() { return new F(arguments); } })();
#1st floor
Any function (or even a constructor) can use a variable number of parameters.Each function has a parameter variable that can be converted to an array using [].slice.call(arguments).
function Something(){ this.options = [].slice.call(arguments); this.toString = function (){ return this.options.toString(); }; } var s = new Something(1, 2, 3, 4); console.log( 's.options === "1,2,3,4":', (s.options == '1,2,3,4') ); var z = new Something(9, 10, 11); console.log( 'z.options === "9,10,11":', (z.options == '9,10,11') );
The above tests produce the following output:
s.options === "1,2,3,4": true z.options === "9,10,11": true
#2nd floor
@Matthew I think it's best to fix the constructor properties as well.
// Invoke new operator with arbitrary arguments // Holy Grail pattern function invoke(constructor, args) { var f; function F() { // constructor returns **this** return constructor.apply(this, args); } F.prototype = constructor.prototype; f = new F(); f.constructor = constructor; return f; }
#3rd floor
If you are interested in an evaluation-based solution
function createSomething() { var q = []; for(var i = 0; i < arguments.length; i++) q.push("arguments[" + i + "]"); return eval("new Something(" + q.join(",") + ")"); }
#4th floor
You can move init East-West to a separate method of the Something prototype:
function Something() { // Do nothing } Something.prototype.init = function() { // Do init stuff }; function createSomething() { var s = new Something(); s.init.apply(s, arguments); return s; } var s = createSomething(a,b,c); // 's' is an instance of Something
#5th floor
This is a generic solution that can call any constructor with an array of parameters (when a function is called, the behavior of the native constructor is different, such as String, Number, Date, and so on).
function construct(constructor, args) { function F() { return constructor.apply(this, args); } F.prototype = constructor.prototype; return new F(); }
Objects created by calling construct(Class, [1, 2, 3]) are the same as those created by using new Class(1, 2, 3).
You can also make a more specific version, so you don't have to go through the constructor every time.This is also slightly more efficient because it does not require a new instance of an internal function to be created every time it is called.
var createSomething = (function() { function F(args) { return Something.apply(this, args); } F.prototype = Something.prototype; return function(args) { return new F(args); } })();
The reason for creating and calling external anonymous functions is to prevent function F from polluting the global namespace.Sometimes called module mode.
[Update]
For those who want to use it in TypeScript, TS will give an error if F returns anything:
function construct(constructor, args) { function F() : void { constructor.apply(this, args); } F.prototype = constructor.prototype; return new F(); }