In JavaScript, I want to create an object instance (via the new operator), but pass an arbitrary number of arguments to the constructor. Is this possible?
What I want to do is something like this (but the code below does not 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
The Answer
From the responses here, it became clear that there's no built-in way to call .apply() with the new operator. However, people suggested a number of really interesting solutions to the problem.
My preferred solution was this one from Matthew Crumley (I've 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);
}
})();
With ECMAScript5's Function.prototype.bind things get pretty clean:
function newCall(Cls) {
return new (Function.prototype.bind.apply(Cls, arguments));
// or even
// return new (Cls.bind.apply(Cls, arguments));
// if you know that Cls.bind has not been overwritten
}
It can be used as follows:
var s = newCall(Something, a, b, c);
or even directly:
var s = new (Function.prototype.bind.call(Something, null, a, b, c));
var s = new (Function.prototype.bind.apply(Something, [null, a, b, c]));
This and the eval-based solution are the only ones that always work, even with special constructors like Date:
var date = newCall(Date, 2012, 1);
console.log(date instanceof Date); // true
edit
A bit of explanation:
We need to run new on a function that takes a limited number of arguments. The bind method allows us to do it like so:
var f = Cls.bind(anything, arg1, arg2, ...);
result = new f();
The anything parameter doesn't matter much, since the new keyword resets f's context. However, it is required for syntactical reasons. Now, for the bind call: We need to pass a variable number of arguments, so this does the trick:
var f = Cls.bind.apply(Cls, [anything, arg1, arg2, ...]);
result = new f();
Let's wrap that in a function. Cls is passed as argument 0, so it's gonna be our anything.
function newCall(Cls /*, arg1, arg2, ... */) {
var f = Cls.bind.apply(Cls, arguments);
return new f();
}
Actually, the temporary f variable is not needed at all:
function newCall(Cls /*, arg1, arg2, ... */) {
return new (Cls.bind.apply(Cls, arguments))();
}
Finally, we should make sure that bind is really what we need. (Cls.bind may have been overwritten). So replace it by Function.prototype.bind, and we get the final result as above.
Here's a generalized solution that can call any constructor (except native constructors that behave differently when called as functions, like String, Number, Date, etc.) with an array of arguments:
function construct(constructor, args) {
function F() {
return constructor.apply(this, args);
}
F.prototype = constructor.prototype;
return new F();
}
An object created by calling construct(Class, [1, 2, 3]) would be identical to an object created with new Class(1, 2, 3).
You could also make a more specific version so you don't have to pass the constructor every time. This is also slightly more efficient, since it doesn't need to create a new instance of the inner function every time you call it.
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 the outer anonymous function like that is to keep function F from polluting the global namespace. It's sometimes called the module pattern.
[UPDATE]
For those who want to use this in TypeScript, since TS gives an error if F returns anything:
function construct(constructor, args) {
function F() : void {
constructor.apply(this, args);
}
F.prototype = constructor.prototype;
return new F();
}
If your environment supports ECMA Script 2015's spread operator (...), you can simply use it like this
function Something() {
// init stuff
}
function createSomething() {
return new Something(...arguments);
}
Note: Now that the ECMA Script 2015's specifications are published and most JavaScript engines are actively implementing it, this would be the preferred way of doing this.
You can check the Spread operator's support in few of the major environments, here.
Suppose you've got an Items constructor which slurps up all the arguments you throw at it:
function Items () {
this.elems = [].slice.call(arguments);
}
Items.prototype.sum = function () {
return this.elems.reduce(function (sum, x) { return sum + x }, 0);
};
You can create an instance with Object.create() and then .apply() with that instance:
var items = Object.create(Items.prototype);
Items.apply(items, [ 1, 2, 3, 4 ]);
console.log(items.sum());
Which when run prints 10 since 1 + 2 + 3 + 4 == 10:
$ node t.js
10
In ES6, Reflect.construct() is quite convenient:
Reflect.construct(F, args)
#Matthew
I think it's better to fix the constructor property also.
// 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;
}
You could move the init stuff out into a separate method of Something's 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
An improved version of #Matthew's answer. This form has the slight performance benefits obtained by storing the temp class in a closure, as well as the flexibility of having one function able to be used to create any class
var applyCtor = function(){
var tempCtor = function() {};
return function(ctor, args){
tempCtor.prototype = ctor.prototype;
var instance = new tempCtor();
ctor.prototype.constructor.apply(instance,args);
return instance;
}
}();
This would be used by calling applyCtor(class, [arg1, arg2, argn]);
This answer is a little late, but figured anyone who sees this might be able to use it. There is a way to return a new object using apply. Though it requires one little change to your object declaration.
function testNew() {
if (!( this instanceof arguments.callee ))
return arguments.callee.apply( new arguments.callee(), arguments );
this.arg = Array.prototype.slice.call( arguments );
return this;
}
testNew.prototype.addThem = function() {
var newVal = 0,
i = 0;
for ( ; i < this.arg.length; i++ ) {
newVal += this.arg[i];
}
return newVal;
}
testNew( 4, 8 ) === { arg : [ 4, 8 ] };
testNew( 1, 2, 3, 4, 5 ).addThem() === 15;
For the first if statement to work in testNew you have to return this; at the bottom of the function. So as an example with your code:
function Something() {
// init stuff
return this;
}
function createSomething() {
return Something.apply( new Something(), arguments );
}
var s = createSomething( a, b, c );
Update: I've changed my first example to sum any number of arguments, instead of just two.
I just came across this problem, and I solved it like this:
function instantiate(ctor) {
switch (arguments.length) {
case 1: return new ctor();
case 2: return new ctor(arguments[1]);
case 3: return new ctor(arguments[1], arguments[2]);
case 4: return new ctor(arguments[1], arguments[2], arguments[3]);
//...
default: throw new Error('instantiate: too many parameters');
}
}
function Thing(a, b, c) {
console.log(a);
console.log(b);
console.log(c);
}
var thing = instantiate(Thing, 'abc', 123, {x:5});
Yeah, it's a bit ugly, but it solves the problem, and it's dead simple.
if you're interested in an eval-based solution
function createSomething() {
var q = [];
for(var i = 0; i < arguments.length; i++)
q.push("arguments[" + i + "]");
return eval("new Something(" + q.join(",") + ")");
}
This works!
var cls = Array; //eval('Array'); dynamically
var data = [2];
new cls(...data);
See also how CoffeeScript does it.
s = new Something([a,b,c]...)
becomes:
var s;
s = (function(func, args, ctor) {
ctor.prototype = func.prototype;
var child = new ctor, result = func.apply(child, args);
return Object(result) === result ? result : child;
})(Something, [a, b, c], function(){});
This constructor approach works both with and without the new keyword:
function Something(foo, bar){
if (!(this instanceof Something)){
var obj = Object.create(Something.prototype);
return Something.apply(obj, arguments);
}
this.foo = foo;
this.bar = bar;
return this;
}
It assumes support for Object.create but you could always polyfill that if you're supporting older browsers. See the support table on MDN here.
Here's a JSBin to see it in action with console output.
Solution without ES6 or polyfills:
var obj = _new(Demo).apply(["X", "Y", "Z"]);
function _new(constr)
{
function createNamedFunction(name)
{
return (new Function("return function " + name + "() { };"))();
}
var func = createNamedFunction(constr.name);
func.prototype = constr.prototype;
var self = new func();
return { apply: function(args) {
constr.apply(self, args);
return self;
} };
}
function Demo()
{
for(var index in arguments)
{
this['arg' + (parseInt(index) + 1)] = arguments[index];
}
}
Demo.prototype.tagged = true;
console.log(obj);
console.log(obj.tagged);
output
Demo {arg1: "X", arg2: "Y", arg3: "Z"}
... or "shorter" way:
var func = new Function("return function " + Demo.name + "() { };")();
func.prototype = Demo.prototype;
var obj = new func();
Demo.apply(obj, ["X", "Y", "Z"]);
edit:
I think this might be a good solution:
this.forConstructor = function(constr)
{
return { apply: function(args)
{
let name = constr.name.replace('-', '_');
let func = (new Function('args', name + '_', " return function " + name + "() { " + name + "_.apply(this, args); }"))(args, constr);
func.constructor = constr;
func.prototype = constr.prototype;
return new func(args);
}};
}
You can't call a constructor with a variable number of arguments like you want with the new operator.
What you can do is change the constructor slightly. Instead of:
function Something() {
// deal with the "arguments" array
}
var obj = new Something.apply(null, [0, 0]); // doesn't work!
Do this instead:
function Something(args) {
// shorter, but will substitute a default if args.x is 0, false, "" etc.
this.x = args.x || SOME_DEFAULT_VALUE;
// longer, but will only put in a default if args.x is not supplied
this.x = (args.x !== undefined) ? args.x : SOME_DEFAULT_VALUE;
}
var obj = new Something({x: 0, y: 0});
Or if you must use an array:
function Something(args) {
var x = args[0];
var y = args[1];
}
var obj = new Something([0, 0]);
Matthew Crumley's solutions in CoffeeScript:
construct = (constructor, args) ->
F = -> constructor.apply this, args
F.prototype = constructor.prototype
new F
or
createSomething = (->
F = (args) -> Something.apply this, args
F.prototype = Something.prototype
return -> new Something arguments
)()
function createSomething() {
var args = Array.prototype.concat.apply([null], arguments);
return new (Function.prototype.bind.apply(Something, args));
}
If your target browser doesn't support ECMAScript 5 Function.prototype.bind, the code won't work. It is not very likely though, see compatibilty table.
modified #Matthew answer. Here I can pass any number of parameters to function as usual (not array). Also 'Something' is not hardcoded into:
function createObject( constr ) {
var args = arguments;
var wrapper = function() {
return constr.apply( this, Array.prototype.slice.call(args, 1) );
}
wrapper.prototype = constr.prototype;
return new wrapper();
}
function Something() {
// init stuff
};
var obj1 = createObject( Something, 1, 2, 3 );
var same = new Something( 1, 2, 3 );
This one-liner should do it:
new (Function.prototype.bind.apply(Something, [null].concat(arguments)));
While the other approaches are workable, they're unduly complex. In Clojure you generally create a function that instantiates types/records and use that function as the mechanism for instantiation. Translating this to JavaScript:
function Person(surname, name){
this.surname = surname;
this.name = name;
}
function person(surname, name){
return new Person(surname, name);
}
By taking this approach you avoid the use of new except as described above. And this function, of course, has no issues working with apply or any number of other functional programming features.
var doe = _.partial(person, "Doe");
var john = doe("John");
var jane = doe("Jane");
By using this approach, all of your type constructors (e.g. Person) are vanilla, do-nothing constructors. You just pass in arguments and assign them to properties of the same name. The hairy details go in the constructor function (e.g. person).
It is of little bother having to create these extra constructor functions since they are a good practice anyhow. They can be convenient since they allow you to potentially have several constructor functions with different nuances.
It's also intresting to see how the issue of reusing the temporary F() constructor, was addressed by using arguments.callee, aka the creator/factory function itself:
http://www.dhtmlkitchen.com/?category=/JavaScript/&date=2008/05/11/&entry=Decorator-Factory-Aspect
Any function (even a constructor) can take a variable number of arguments. Each function has an "arguments" variable which can be cast to an array with [].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
Here is my version of createSomething:
function createSomething() {
var obj = {};
obj = Something.apply(obj, arguments) || obj;
obj.__proto__ = Something.prototype; //Object.setPrototypeOf(obj, Something.prototype);
return o;
}
Based on that, I tried to simulate the new keyword of JavaScript:
//JavaScript 'new' keyword simulation
function new2() {
var obj = {}, args = Array.prototype.slice.call(arguments), fn = args.shift();
obj = fn.apply(obj, args) || obj;
Object.setPrototypeOf(obj, fn.prototype); //or: obj.__proto__ = fn.prototype;
return obj;
}
I tested it and it seems that it works perfectly fine for all scenarios. It also works on native constructors like Date. Here are some tests:
//test
new2(Something);
new2(Something, 1, 2);
new2(Date); //"Tue May 13 2014 01:01:09 GMT-0700" == new Date()
new2(Array); //[] == new Array()
new2(Array, 3); //[undefined × 3] == new Array(3)
new2(Object); //Object {} == new Object()
new2(Object, 2); //Number {} == new Object(2)
new2(Object, "s"); //String {0: "s", length: 1} == new Object("s")
new2(Object, true); //Boolean {} == new Object(true)
Yes we can, javascript is more of prototype inheritance in nature.
function Actor(name, age){
this.name = name;
this.age = age;
}
Actor.prototype.name = "unknown";
Actor.prototype.age = "unknown";
Actor.prototype.getName = function() {
return this.name;
};
Actor.prototype.getAge = function() {
return this.age;
};
when we create an object with "new" then our created object INHERITS getAge(), But if we used apply(...) or call(...) to call Actor, then we are passing an object for "this" but the object we pass WON'T inherit from Actor.prototype
unless, we directly pass apply or call Actor.prototype but then.... "this" would point to "Actor.prototype" and this.name would write to: Actor.prototype.name. Thus affecting all other objects created with Actor...since we overwrite the prototype rather than the instance
var rajini = new Actor('Rajinikanth', 31);
console.log(rajini);
console.log(rajini.getName());
console.log(rajini.getAge());
var kamal = new Actor('kamal', 18);
console.log(kamal);
console.log(kamal.getName());
console.log(kamal.getAge());
Let's try with apply
var vijay = Actor.apply(null, ["pandaram", 33]);
if (vijay === undefined) {
console.log("Actor(....) didn't return anything
since we didn't call it with new");
}
var ajith = {};
Actor.apply(ajith, ['ajith', 25]);
console.log(ajith); //Object {name: "ajith", age: 25}
try {
ajith.getName();
} catch (E) {
console.log("Error since we didn't inherit ajith.prototype");
}
console.log(Actor.prototype.age); //Unknown
console.log(Actor.prototype.name); //Unknown
By passing Actor.prototype to Actor.call() as the first argument, when the Actor() function is ran, it executes this.name=name, Since "this" will point to Actor.prototype, this.name=name; means Actor.prototype.name=name;
var simbhu = Actor.apply(Actor.prototype, ['simbhu', 28]);
if (simbhu === undefined) {
console.log("Still undefined since the function didn't return anything.");
}
console.log(Actor.prototype.age); //simbhu
console.log(Actor.prototype.name); //28
var copy = Actor.prototype;
var dhanush = Actor.apply(copy, ["dhanush", 11]);
console.log(dhanush);
console.log("But now we've corrupted Parent.prototype in order to inherit");
console.log(Actor.prototype.age); //11
console.log(Actor.prototype.name); //dhanush
Coming back to orginal question how to use new operator with apply, here is my take....
Function.prototype.new = function(){
var constructor = this;
function fn() {return constructor.apply(this, args)}
var args = Array.prototype.slice.call(arguments);
fn.prototype = this.prototype;
return new fn
};
var thalaivar = Actor.new.apply(Parent, ["Thalaivar", 30]);
console.log(thalaivar);
since ES6 this is possible through the Spread operator, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator#Apply_for_new
This answer was already, sort of given in comment https://stackoverflow.com/a/42027742/7049810, but seems to have been missed by most
Actually the simplest method is:
function Something (a, b) {
this.a = a;
this.b = b;
}
function createSomething(){
return Something;
}
s = new (createSomething())(1, 2);
// s == Something {a: 1, b: 2}
A revised solution from #jordancpaul's answer.
var applyCtor = function(ctor, args)
{
var instance = new ctor();
ctor.prototype.constructor.apply(instance, args);
return instance;
};
Make an anonymous prototype and apply the Something prototype to it using the arguments and then create a new instance of that anonymous prototype. The one disadavantage of this is it will not pass the s instanceof Something check, though it is identical, it is basically an instance of a clone.
function Something(){
// init stuff
}
function createSomething(){
return new (function(){Something.apply(this, arguments)});
}
var s = createSomething(a,b,c); // 's' is an instance of Something
function FooFactory() {
var prototype, F = function(){};
function Foo() {
var args = Array.prototype.slice.call(arguments),
i;
for (i = 0, this.args = {}; i < args.length; i +=1) {
this.args[i] = args[i];
}
this.bar = 'baz';
this.print();
return this;
}
prototype = Foo.prototype;
prototype.print = function () {
console.log(this.bar);
};
F.prototype = prototype;
return Foo.apply(new F(), Array.prototype.slice.call(arguments));
}
var foo = FooFactory('a', 'b', 'c', 'd', {}, function (){});
console.log('foo:',foo);
foo.print();
Related
I have class Foo, and want to make a function FooPrime which is equivalent to call new Foo() with the arguments passed to FooPrime;
In other words, if I do:
FooPrime(1, 2, 3);
it should be the same as calling to:
new Foo(1, 2, 3)
Normally I'd use call or apply for this sort of thing, and I can get almost the same effect using apply:
var FooPrime = function() {
return Foo.apply({}, arguments);
}
However:
Foo.apply({}, [1, 2, 3]);
is not exactly the same as calling:
new Foo(1, 2, 3)
so my question is, is there any way to make a function which does exactly the same thing as new, with any number of arguments?
In ECMAScript 5 you can polyfill [[Construct]] with something like this:
function isObject(o) {
return Object(o) === o;
}
function construct(F, args) {
var obj = isObject(F.prototype) ? Object.create(Foo.prototype) : {},
res = Function.prototype.apply.call(F, obj, args);
return isObject(res) ? res : obj;
}
var FooPrime = function() {
return construct(Foo, arguments);
}
ECMAScript 6 provides the simpler and more reliable Reflect.construct
var FooPrime = function() {
return Reflect.construct(Foo, arguments);
}
Or you can also use the spread operator, which has more browser support than Reflect.
var FooPrime = function() {
return new Foo(...arguments);
}
Ruby's refinements allow you to temporarily "upgrade" an object within a lexical scope. I'm trying to implement a similar idea in javascript. Here's some working code that does almost what I want:
function dateExtension() {
return {
day: function() { return this.getDate() }
}
}
function refine(ctor, mixin) {
return function() {
var ret = new (Function.prototype.bind.apply(ctor, arguments));
return Object.assign(ret, mixin);
}
}
function test() {
// Cant overwrite native Date function,
// so have to rename it.
var XDate = refine(Date, dateExtension());
var d = new XDate();
console.log(d.day()); // prints the day of the month to console
}
test();
What I really want to do is this:
function test() {
var Date = refine(Date, dateExtension());
var d = new Date();
console.log(d.day()); // Uncaught TypeError: Bind must be called on a function
}
The idea would be to make the local var Date overide the built in Date, within the body of test() only. So that, within test(), it would acquire the new method day(), but outside of test() Date would be unaffected. This apparently is not allowed.
Is there some workaround to make this idea work?
I made a tiny library called chill-patch to do exactly that. It enables you to use functions as methods, and to safely patch prototypes.
Here's an example of how to use it:
const chillPatch = require('chill-patch')
const lastFunc = arr => arr[arr.length - 1]
const array = [1, 2, 3]
// safely add a method to `Array`
const last = chillPatch(Array, lastFunc, 'last')
// call the new method!
array[last]() //=> 3
If you want to roll your own, the entire source code is as follows:
'use strict'
const chillPatch = (klass, func, optionalDescription) => {
const symbol = Symbol(optionalDescription)
klass.prototype[symbol] = function(){
const args = Array.prototype.slice.call(arguments)
args.unshift(this)
return func.apply(null, args)
}
return symbol
};
module.exports = chillPatch
I see you said in a comment that you do not want to modify prototypes, but your reason is probably that modifying prototypes is dangerous. However, the method above patches using Symbol, which is completely safe. The change will be invisible to other parts of the codebase unless someone is doing reflection with Object.getOwnPropertySymbols()
So I came up with something that works, although it's fairly hacky.
Here's a fiddle: https://jsfiddle.net/40cty4qa/
And here's the code
function lg() { console.log.apply(console, arguments) }
function extend(o, mixin) {
for (var k in mixin) o.prototype[k] = mixin[k]
}
function unextend(o, mixin) {
for (var k in mixin) delete o.prototype[k];
}
Function.prototype.refine = function(ctor, mixin) {
var self = this;
return function() {
extend(ctor, mixin);
var ret = self.apply(this, arguments);
unextend(ctor, mixin);
return ret;
}
}
function dateExtension() {
return {
day: function() { return this.getDate() }
}
}
function refine(ctor, mixin) {
return function() {
var ret = new (Function.prototype.bind.apply(ctor, arguments));
return Object.assign(ret, mixin);
}
}
function test() {
var d = new Date();
lg(d.day());
}
test = test.refine(Date, dateExtension());
test(); // This works, since were inside a refinement
var d = new Date();
lg(d.day()); // This doesnt work, as desired.
I have the following code:
function Rune(){
this.subSpells = [];
}
function Modifier(){
this.type = "modifier";
}
Modifier.prototype = new Rune();
function RuneFactory(effect, inheritsFrom, initialValue){
var toReturn = function(){};
toReturn.prototype = new inheritsFrom();
toReturn.prototype.subSpells[effect] = initialValue;
return toReturn;
}
Duration = RuneFactory("duration", Modifier, 1);
Quicken = RuneFactory("quicken", Modifier, 1);
x = new Duration();
y = new Quicken();
Both x.subSpells.duration and x.subSpells.quicken equal 1. Same with y.
I want x.subSpells.quicken and y.subSpells.duration to be undefined.
If I do the following for the Duration and Quicken definitions, I get the behaviour I want.
Duration = RuneFactory("duration", Rune, 1);
Quicken = RuneFactory("quicken", Rune, 1);
I think there is a problem with double inheritance. Can anyone tell me how to change my RuneFactory code such that it works with double inheritance and/or explain what's breaking? If at all possible, I would like to avoid using a framework.
Thank you!
Add following line in the Modifier function Rune.apply(this, arguments);, since subSpells defined at prototype level, all instance of Rune refer to same object.
And, change type of subSpells from array to object.
As mentioned by wared would be better if you use Parasitic Combination Inheritance Pattern.
"I think there is a problem with double inheritance" - No!
At first, there's no such thing as double inheritance, you have same problem as with Modifier constructor, your instances refer to same subSpells object (Array in your case. read about Primitive value vs Reference value or References and Values it's about .NET, but basic principles are the same in programming).
Look at How does JavaScript .prototype work?
Look at jsFiddle
function inheritPrototype(subType, superType) {
var prototype = Object.create(superType.prototype, {
constructor: {
value: subType,
enumerable: true
}
});
subType.prototype = prototype;
}
function Rune() {
this.subSpells = {};
}
function Modifier() {
Rune.apply(this, arguments);
this.type = "modifier";
}
inheritPrototype(Modifier, Rune);
function RuneFactory(effect, inheritsFrom, initialValue) {
function toReturn() {
inheritsFrom.apply(this, arguments); // you should bind this
this.subSpells[effect] = initialValue;
}
inheritPrototype(toReturn, inheritsFrom);
return toReturn;
}
Duration = RuneFactory("duration", Modifier, 1);
Quicken = RuneFactory("quicken", Modifier, 1);
x = new Duration();
y = new Quicken();
console.log(x.subSpells.duration); // 1
console.log(x.subSpells.quicken); // undefined
console.log(y.subSpells.duration); // undefined
console.log(y.subSpells.quicken); // 1
The correct way to inherit in JavaScript is:
function Parent(arg1, arg2) {
// Do something
}
function Child(arg1, arg2) {
Parent.call(this, arg1, arg2); // or Parent.apply(this, arguments)
}
Child.prototype = Object.create(Parent.prototype, {
constructor: {
value: Child
}
});
So you will need something like this:
function Rune(){
this.subSpells = {};
}
function Modifier(){
Rune.apply(this, arguments);
this.type = "modifier";
}
Modifier.prototype = Object.create(Rune.prototype, {
constructor: {
value: Modifier
}
});
function RuneFactory(effect, inheritsFrom, initialValue){
var toReturn = function(){};
toReturn.prototype = Object.create(inheritsFrom.prototype, {
constructor: {
value: Modifier
}
});
toReturn.prototype.subSpells = {};
toReturn.prototype.subSpells[effect] = initialValue;
return toReturn;
}
Duration = RuneFactory("duration", Modifier, 1);
Quicken = RuneFactory("quicken", Modifier, 1);
x = new Duration();
y = new Quicken();
NOTE: in your question .subSpells is an array but you assign an member as it would be an object!
I know the possibility to call a function with an array of arguments with apply(obj,args);
Is there a way to use this feature when creating a new instance of a function?
I mean something like this:
function A(arg1,arg2){
var a = arg1;
var b = arg2;
}
var a = new A.apply([1,2]); //create new instance using an array of arguments
I hope you understand what i mean... ^^^
Thanks for your help!
Solved!
I got the right answer. To make the answer fit to my question:
function A(arg1,arg2) {
var a = arg1;
var b = arg2;
}
var a = new (A.bind.apply(A,[A,1,2]))();
var wrapper = function(f, args) {
return function() {
f.apply(this, args);
};
};
function Constructor() {
this.foo = 4;
}
var o = new (wrapper(Constructor, [1,2]));
alert(o.foo);
We take a function and arguments and create a function that applies the arguments to that function with the this scope.
Then if you call it with the new keyword it passes in a new fresh this and returns it.
The important thing is the brackets
new (wrapper(Constructor, [1,2]))
Calls the new keyword on the function returned from the wrapper, where as
new wrapper(Constructor, [1,2])
Calls the new keyword on the wrapper function.
The reason it needs to be wrapped is so that this that you apply it to is set with the new keyword. A new this object needs to be created and passed into a function which means that you must call .apply(this, array) inside a function.
Live example
Alternatively you could use ES5 .bind method
var wrapper = function(f, args) {
var params = [f].concat(args);
return f.bind.apply(f, params);
};
See example
with ECMAscript 5 you can:
function createInstanceWithArguments (fConstructor, aArgs) {
var foo = Object.create(fConstructor.prototype);
fConstructor.apply(foo, aArgs);
return foo;
}
#Raynos answer works well, except that the non-ES5 version is missing the constructor's prototype after instantiation.
Here's my updated cApply method:
var cApply = function(c) {
var ctor = function(args) {
c.apply(this, args);
};
ctor.prototype = c.prototype;
return ctor;
};
Which can be used like this:
var WrappedConstructor = cApply(Constructor);
var obj = new WrappedConstructor([1,2,3]);
// or inline like this.
var obj2 = new (cApply(Constructor))([1,2,3]);
JSFiddle for reference.
You can curry the functions:
function A(arg1, arg2) {
// ...
}
function A12() {
A(1, 2);
}
You could even build a curry factory:
function A_curry() {
var args = arguments;
return function () {
A.apply(null, args);
};
}
You can use Object.create() to build the new instance, and call the constructor on the instance after that:
var args = [1,2,3];
var instance = Object.create(MyClass.prototype);
MyClass.apply(instance, args);
or in a single line:
var instance = MyClass.apply(Object.create(MyClass.prototype), args);
but don't forget return this; in MyClass by this single line solution.
If you don't have Object.create(), then it is very simple to write one:
Object.create = function (source){
var Surrogate = function (){};
Surrogate.prototype = source;
return new Surrogate();
};
You can optionally write an Function.newInstance() or a Function.prototype.newInstance() function:
Function.newInstance = function (fn, args){
var instance = Object.create(fn.prototype);
fn.apply(instance, args);
return instance;
};
so var instance = Function.newInstance(MyClass, args);.
note: It is not recommended to override native classes.
What if you have your object class name stored in a variable called className ?
var className = 'A'
According to this answer here it all becomes very clean and simple using ECMAScipt5's Function.prototype.bind method.
With an array of arguments:
new ( Function.prototype.bind.apply( className, arguments ) );
Or with an argument list:
new ( Function.prototype.bind.call( className, argument1, argument2, ... ) );
A single line, no need for a wrapper.
I just had the same issue and also wanted to preserve prototype properties of the target object. After looking at the answers, just came up with a rather simple solution. Thought I might as well share it for any future seeker :)
// Define a pseudo object that takes your arguments as an array
var PseudoObject = function (args) {
TargetObject.apply(this, args);
};
// if you want to inherit prototype as well
PseudoObject.prototype = new TargetObject();
// Now just use the PseudoObject to instantiate a object of the the OtherObject
var newObj = new PseudoObject([1, 2, 3]);
It can now be done using the spread operator:
let a = new A(...[1,2]);
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#examples
A nasty gotcha in javascript is forgetting to call new on a function meant to be instantiated, leading to this being bound to a different object (usually the global) instead of a fresh one. One workaround I read about is to check for it explicitly in the function-constructor using the following idiom:
function SomeConstructor(x, y, ...) {
// check if `this` is created with new
if ( !(this instanceof arguments.callee) )
return new SomeConstructor(x, y, ...);
// normal initialization code follows
Now new SomeConstructor(...) and SomeConstructor(...) are equivalent.
I'd like to simplify this by creating a wrapper function factory(fn) that does the first two lines and then delegates to the wrapped function fn. This would be used like:
SomeConstructor = factory(function (x, y, ...) {
// normal initialization code follows
})
My first attempt was:
function factory(fn) {
return function() {
if ( !(this instanceof arguments.callee) ) {
return new arguments.callee.apply(this, arguments);
}
fn.apply(this, arguments);
}
}
but it fails with "Function.prototype.apply called on incompatible [object Object]". The second attempt was:
function factory(fn) {
return function() {
if ( !(this instanceof arguments.callee) ) {
var tmp = new arguments.callee();
arguments.callee.apply(tmp, arguments);
return tmp;
}
fn.apply(this, arguments);
}
}
This sort of works but it may call the wrapped function twice: once with no arguments (to create a new instance) and once with the passed arguments for the actual initialization. Apparently this is fragile and inefficient but I can't figure out a way to do it with a single call. Is this possible ?
EDIT: Based on bobince's approach, here's a similar one that does the trick:
function factory(init) {
var run_init = true;
function constr() {
if ( !(this instanceof constr) ) {
run_init = false;
var tmp = new constr();
run_init = true;
init.apply(tmp, arguments);
return tmp;
}
if (run_init)
init.apply(this, arguments);
}
return constr;
}
As for whether this is something that should be encouraged or not, that's debatable. I come from a Python background and I think of new as just noise (Java) or wart (Javascript), but I may be missing something.
This simply encourages a bad-habit shortcut that relies far too heavily on the implementation of the class to "fix" the calling code.
If this is a problem, don't just let it slide, throw an error message.
You can pass a unique value into the constructor for the first call (with new) that signifies you don't want the initialiser called yet:
var _NOINIT= {};
function factory(init) {
function constr() {
if (!(this instanceof constr)) {
var inst= new constr(_NOINIT);
init.apply(inst, arguments);
return inst;
}
if (arguments[0]!==_NOINIT)
init.apply(this, arguments);
}
return constr;
}
Note I've used a named inline function for the constructor because arguments.callee will be going away in ECMAScript Fifth Edition's ‘strict’ mode.
However if you're using a class factory, I suggest making the initialiser function a member of the class, rather than being passed in. That way, you can subclass a base class and have the subclass inherit the initialiser, which is normal behaviour in class-based languages. eg.:
Function.prototype.makeSubclass= function() {
function constr() {
var that= this;
if (!(this instanceof constr))
that= new constr(_NOINIT);
if (arguments[0]!==_NOINIT && '_init' in that)
that._init.apply(that, arguments);
return that;
}
if (this!==Object)
constr.prototype= new this(_NOINIT);
return constr;
};
var Shape= Object.makeSubclass();
Shape.prototype._init= function(x, y) {
this.x= x;
this.y= y;
};
var Point= Shape.makeSubclass();
// inherits initialiser(x, y), as no need for anything else in there
var Circle= Shape.makeSubclass()
Circle.prototype._init= function(x, y, r) {
Shape.prototype._init.call(this, x, y);
this.r= r;
};
Of course you don't have to put that into the Function prototype... it's a matter of taste, really. As is allowing constructors without new.
Personally I prefer to throw an error rather than silently make it work, to try to discourage bare-constructor-calling, since this is a mistake elsewhere and may make the code slightly less clear.
Your "factory" function could be written in this way:
function factory(fn, /* arg1, arg2, ..., argn */) {
var obj = new fn(), // Instantiate using 'new' to preserve the prototype chain
args = Array.prototype.slice.call(arguments, 1); // remove fn argument
fn.apply(obj, args); // apply the constructor again, with the right arguments
return obj;
}
// Test usage:
function SomeConstructor (foo, bar) {
this.foo = foo;
this.bar = bar;
}
SomeConstructor.prototype.test = true;
var o = factory(SomeConstructor, 'foo', 'bar');
// will return: Object foo=foo bar=bar test=true, and
o instanceof SomeConstructor; // true
However, the new operator is not bad, I would not encourage you to avoid it, I would recommend you to stick with a proper naming convention, constructor functions identifiers in PascalCase, all other identifiers in camelCase, and also I highly recommend you to use JsLint it will help you to detect that kind of mistakes early.
I dislike your mixing of arguments.callee and the function's identifier. Also, you are dumbing down the original problem. You should have used apply to begin with so as not to make the helper (factory) function seem even better than it really is.
What should have been done to begin with:
function SomeConstructor(x, y, ...) {
// check if `this` is created with new
if ( !(this instanceof arguments.callee) )
return new arguments.callee.apply (this, arguments);
// normal initialization code follows
Another issue with factory is that it defeats the function's length property.
while 'new' is a good thing, and I don't endorse trying to do away with language features, check out this code I played with a while ago: (note, this is not a complete solution for you, but rather something to build into your code)
function proxy(obj)
{
var usingNew = true;
var obj_proxy = function()
{
if (usingNew)
this.constructor_new.apply(this, arguments);
};
obj_proxy.prototype = obj.prototype;
obj_proxy.prototype.constructor_new = obj.prototype.constructor;
obj_proxy.createInstance = function()
{
usingNew = false;
var instance = new obj_proxy();
instance.constructor_new.apply(instance, arguments);
usingNew = true;
return instance;
}
return obj_proxy;
}
to test it out, create a function foo like this:
function foo(a, b) { this.a = a; }
and test it:
var foo1 = proxy(foo);
var test1 = new foo1(1);
alert(test1 instanceof foo);
var test2 = foo1.createInstance(2);
alert(test2 instanceof foo);
EDIT: removed some code not relevant for this.
If you are interested in dealing with the inability to use apply with new, one could use
Function.prototype.New = (function () {
var fs = [];
return function () {
var f = fs [arguments.length];
if (f) {
return f.apply (this, arguments);
}
var argStrs = [];
for (var i = 0; i < arguments.length; ++i) {
argStrs.push ("a[" + i + "]");
}
f = new Function ("var a=arguments;return new this(" + argStrs.join () + ");");
if (arguments.length < 100) {
fs [arguments.length] = f;
}
return f.apply (this, arguments);
};
}) ();
Example:
var foo = Foo.New.apply (null, argArray);
Here is some broilerplate code I've come up with as a code-template for object factory in AngularjS. I've used a Car/CarFactory as an example to illustrate. Makes for simple implementation code in the controller.
<script>
angular.module('app', [])
.factory('CarFactory', function() {
/**
* BroilerPlate Object Instance Factory Definition / Example
*/
this.Car = function(color) {
// initialize instance properties
angular.extend(this, {
color : null,
numberOfDoors : null,
hasFancyRadio : null,
hasLeatherSeats : null
});
// generic setter (with optional default value)
this.set = function(key, value, defaultValue, allowUndefined) {
// by default,
if (typeof allowUndefined === 'undefined') {
// we don't allow setter to accept "undefined" as a value
allowUndefined = false;
}
// if we do not allow undefined values, and..
if (!allowUndefined) {
// if an undefined value was passed in
if (value === undefined) {
// and a default value was specified
if (defaultValue !== undefined) {
// use the specified default value
value = defaultValue;
} else {
// otherwise use the class.prototype.defaults value
value = this.defaults[key];
} // end if/else
} // end if
} // end if
// update
this[key] = value;
// return reference to this object (fluent)
return this;
}; // end this.set()
}; // end this.Car class definition
// instance properties default values
this.Car.prototype.defaults = {
color: 'yellow',
numberOfDoors: 2,
hasLeatherSeats: null
};
// instance factory method / constructor
this.Car.prototype.instance = function(params) {
return new
this.constructor()
.set('color', params.color)
.set('numberOfDoors', params.numberOfDoors)
.set('hasFancyRadio', params.hasFancyRadio)
.set('hasLeatherSeats', params.hasLeatherSeats)
;
};
return new this.Car();
}) // end Factory Definition
.controller('testCtrl', function($scope, CarFactory) {
window.testCtrl = $scope;
// first car, is red, uses class default for:
// numberOfDoors, and hasLeatherSeats
$scope.car1 = CarFactory
.instance({
color: 'red'
})
;
// second car, is blue, has 3 doors,
// uses class default for hasLeatherSeats
$scope.car2 = CarFactory
.instance({
color: 'blue',
numberOfDoors: 3
})
;
// third car, has 4 doors, uses class default for
// color and hasLeatherSeats
$scope.car3 = CarFactory
.instance({
numberOfDoors: 4
})
;
// sets an undefined variable for 'hasFancyRadio',
// explicitly defines "true" as default when value is undefined
$scope.hasFancyRadio = undefined;
$scope.car3.set('hasFancyRadio', $scope.hasFancyRadio, true);
// fourth car, purple, 4 doors,
// uses class default for hasLeatherSeats
$scope.car4 = CarFactory
.instance({
color: 'purple'
numberOfDoors: 4
});
// and then explicitly sets hasLeatherSeats to undefined
$scope.hasLeatherSeats = undefined;
$scope.car4.hasLeatherSeats.set('hasLeatherSeats', $scope.hasLeatherSeats, undefined, true);
// in console, type window.testCtrl to see the resulting objects
});
</script>
the only thing that worked for me involves dumbing down the implementation. it's ugly but works (both with and without operator new):
var new_ = function (cls)
{
var constructors = [
function ()
{
return new cls();
}
, function ($0)
{
return new cls($0);
}
, function ($0, $1)
{
return new cls($0, $1);
}
, function ($0, $1, $2)
{
return new cls($0, $1, $2);
}
, // up to a chosen limit
];
return function ()
{
return constructors[arguments.length].apply(
this
, arguments
);
}
}
edit to react to comments
I have way-below-average tolerance to repetitive code, and this code hurt to write, but the functions in constructors need to be separate if arguments.length is to mean something in the real constructor function. consider a variant with a single new wrapper:
var new_ = function (cls)
{
// arbitrary limit
var constructor = function ($0, $1, $2)
{
return new cls($0, $1, $2);
};
return function ()
{
return constructor.apply(
this
, arguments
);
}
}
var gen = new_(function ()
{
print(
arguments.length
+ " "
+ Array.prototype.toSource.call(arguments)
);
});
gen("foo") // 3 ["foo", undefined, undefined]
gen("foo", "bar") // 3 ["foo", "bar", undefined]
gen("foo", "bar", "baz") // 3 ["foo", "bar", "baz"]
the parameter list can be arbitrarily wide, but arguments.length doesn't lie only in the special case.
I've been using this solution with the upper limit of 10 arguments for a few years, and I don't remember ever running into the limit. the risk that it'll ever happen is rather low: everybody knows that functions with many parameters are a no-no, and javascript has a better interface for the desired functionality: packing parameters into objects.
so, the only limit is the width of the parameter list, and this limit seems to be purely theoretical. other than that, it supports completely arbitrary constructors, so I'd say it's very general.