Related
I have a constructor function like this:
function a(p){
this.k=12;
this.f = p;
this.t = function(){};
}
Now let say i create a object with this function:
let obj = new a("tf");
I can always do this to check if a new property assigned to object:
obj = new Proxy(obj,{set:()=>{console.log("new property assigned!");return true;}})
//Now if i were to say
obj.property1 = 12
//It will log out "new property assigned!"
But the problem is, that i have to do this for every new object i create with this constructor function.
So what i want to do is whenever i create a new object like this:
let newObj = new a();
I want to set my proxy automatically.
How can i achieve this?
Write a factory function in which you create a new instance of your object while also adds a Proxy to the newly created instance.
function proxyFactory(Class, ...args) {
const instance = new Class(...args);
return new Proxy(instance, {
set: () => {
console.log("new property assigned!");
return true;
}
});
}
And then call the factory function with the class and the arguments you want to apply.
let newObj = proxyFactory(a, "tf");
You could also make a proxy around class, intercept construct calls, and return a proxy with set trap.
const A = new Proxy(a, {
construct(target, args) {
return new Proxy(new target(...args), {
set() {
console.log('new property assigned');
return true
}
})
}
})
const inst = new A('tf')
inst.property = true
function a(p) {
this.k = 12;
this.f = p;
this.t = function() {};
}
You could make a function that wraps around your class. E.g.
const proxyify = (Class, ...args) => {
return new Proxy(new Class(...args), {
set: () => {
console.log('set');
return true;
}
});
}
Usage would then be
function Dog() {
this.type = "Dog";
}
const ProxiedDog = proxify(Dog);
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();
I'm having a bit of a dilemma getting my head around JS' prototypal inheritance. What I'm trying to do is:
Define an object called mod
var mod = function() {
function sayGoodbye() {
alert("Goodbye!");
}
function saySomethingElse(message) {
alert(message);
}
return {
sayGoodbye: sayGoodbye,
saySomethingElse: saySomethingElse
};
};
Define a prototype object called proto
var proto = {
sayHello: function() {
alert("Hello!");
}
};
Set the prototype of mod to proto
mod.prototype = proto;
Call a function that constructs a new instance of mod with the proto prototype
function construct(constructor, args) {
function constructorWrapper() {
return constructor.apply(this, args)
}
constructorWrapper.prototype = constructor.prototype;
return new constructorWrapper();
}
var foo = construct(mod, ["Other Message 1"]);
var bar = construct(mod, ["Other Message 2"]);
console.dir(foo);
console.dir(bar);
The construct function creates a new instance of mod correctly using the apply function but it's prototype is not proto. What am I missing that prevents mod from being constructed with proto as it's prototype?
Here is a fiddle with the above code.
Thanks heaps!!
The reason the .prototype assignment isn't working for you is because setting the prototype chain like this only works when you use the new operator on a constructor function.
You created a factory function that returns a newly created object. Get rid of the return in mod and use this to attach your method and use new operator when creating instances of mod will make the .prototype assignment work.
This might be confusing so I updated your fiddle:
https://jsfiddle.net/6fdo649y/1/
There are several ways to achieve what you are trying to do, but this example explains why you don't see .prototype work.
//Constructor function using this
function Mod(arg1) {
this.sayGoodbye = function sayGoodbye() {
alert("Goodbye!");
}
this.saySomethingElse = function saySomethingElse(message) {
alert(message);
}
this.arg1 = arg1;
};
var proto = {
sayHello: function() {
alert("Hello!");
}
};
Mod.prototype = proto;
function construct(constructor, args) {
function constructorWrapper() {
constructor.apply(this, args)
}
constructorWrapper.prototype = constructor.prototype;
return new constructorWrapper();
}
var foo = construct(Mod, ["Other Message 1"]);
var bar = construct(Mod, ["Other Message 2"]);
console.dir(foo === bar);
console.dir(foo);
console.dir(bar);
edited: added in passing the args through with apply.
I would like to follow the inheritance structure shown above. I would like create an engineer using this syntax:
var Mark = new Employee(id).WorkerBee(project).Engineer();
To achieve this syntax, I have to create a nested object following a parasitic inheritance pattern like so:
function Employee(id) {
this.id = id;
this.WorkerBee = function(project) {
this.project = project;
this.Engineer = function() {
...
return this;
};
return this;
};
}
To avoid deep layers of nesting, I am trying to rewrite it using prototypes. How can I rewrite my code to achieve the same goal as above ?
function Employee(id) {
//variables
this.id = id
this.name = "";
this.dept = "general";
//methods
this.getId = function() {
return this.id
}
}
Employee.prototype.WorkerBee = WorkerBee;
function WorkerBee(project) {
//variables
this.projectName = project
this.projects = [];
//methods
this.getProjectName = function() {
return this.projectName
}
return this
}
WorkerBee.prototype.Engineer = Engineer
function Engineer() {
//variables
this.dept = "engineering";
this.machine = "";
//methods
this.getDept = function() {
return this.dept
}
return this
}
var Mark = new Employee("5").WorkerBee("Secret Project").Engineer();
console.log(Mark.getId()) //should print "5"
console.log(Mark.getProjectName()) //should print "Secret Project"
console.log(Mark.getDept()) //should print engineering
UPDATE:
Ok, I understand partially. What is the reason why you want to do this? Do you just want a shortcut for creating multiple instances using multiple statements?
Should the instance of C returned by A().B().C() be any different than the one created with the standard new C()?
If you just want to chain constructors, you can add the context in which they are defined (most likely the global object) to the prototype chain of the created entities. You should be able to do that:
var A = function () {};
A.prototype = Object.create(this);
What this does not eliminate though is the need for the new keyword for instantiation. You would need to do new (new (new A()).B()).C(). I can't think of a different approach than having a helper function which would create constructors which do not require the new keyword:
var define = function (init) {
var Constructor = function () {
if (!(this instanceof Constructor)) {
return new Constructor();
}
if (init) {
init.apply(this, Array.prototype.slice.call(arguments));
}
};
Constructor.prototype = Object.create(this);
return Constructor;
};
The usage is:
var A = define(function (x, y) {
this.x = x;
this.y = y;
});
var a1 = new A(1, 2);
// a1 instanceof A === true
// a1.x === 1
// a1.y === 2
var a2 = A(1, 2);
// a2 instanceof A === true
// a2.x === 1
// a2.y === 2
If you have the constructors A, B and C, you can use the following notations interchangeably:
var a = new A();
var b = new B();
var c = new C();
var a = A();
var b = B();
var c = C();
var b = A().B();
var c = A().C();
var a = B().C().A();
In case of A().B().C(), you do not have access to the instances of A and B.
Can you elaborate a bit more on what is your deal?
OLD ANSWER:
What you have there is madness as you basically merge three constructors and make it seem that `WorkerBee` and `Employee` are actually instantiated while they are not.
I'm not going to question the new A().B().C() notation even though I find it quite messed up.
You probably want to make use of the instanceof operator the following way.
var A = function (x) {
if (!(this instanceof A)) return new A(x);
this.x = x;
};
var B = function (y) {
if (!(this instanceof B)) return new B(y);
this.y = y;
};
var C = function () {
if (!(this instanceof C)) return new C();
};
A.prototype.B = B;
B.prototype.C = C;
You can now call new A() and A(), new B() and B() and new C() and C() interchangeably while achieving the same result as both of the calls always return an instance of the constructor.
new A() instanceof A === true
new A().B() instanceof B === true
new A().B().C() instanceof C === true
Based on the comments, you seem to have the belief that you have to use this odd, verbose mechanism in order to have inheritance between Employee <- WorkerBee <- Engineer (and such), but you don't; normal inheritance is all you need for this:
// ==== Employee
function Employee(id) {
this.id = id;
}
// Add Employee methods to Employee.prototype, e.g.:
Employee.prototype.getId = function() {
return this.id;
};
// ==== WorkerBee, derived from Employee
function WorkerBee(id, project) {
// Inheritance, part 1: Chain to the base constructor
Employee.call(this, id);
// WorkerBee stuff
this.project = project;
}
// Inheritance, part 2: Create the object to use for WorkerBee
// instance prototypes, using Employee.prototype as its prototype.
WorkerBee.prototype = Object.create(Employee.prototype);
WorkerBee.prototype.constructor = WorkerBee;
// Add WorkerBee methods to WorkerBee.prototype, e.g.:
WorkerBee.prototype.getProjectName = function() {
return this.project;
};
// ==== Engineer, derived from WorkerBee
function Engineer(id, project) {
// Inheritance, part 1: Chain to the base constructor
WorkerBee.call(this, id, project);
}
// Inheritance, part 2: Create the object to use for Engineer
// instance prototypes, using WorkerBee.prototype as its prototype.
Engineer.prototype = Object.create(WorkerBee.prototype);
Engineer.prototype.constructor = Engineer;
// Add Engineer methods to Engineer.prototype, e.g.:
Engineer.prototype.getDept = function() {
return "Engineering";
};
// ==== Usage
var mark = new Engineer("5", "Secret Project");
snippet.log(mark.getId()); // "5"
snippet.log(mark.getProjectName()); // "Secret Project"
snippet.log(mark.getDept()); // "Engineering"
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
That's the standard way to do prototypical inheritance using constructor functions in JavaScript (for now; in ES6, you'd use the new class feature, which does basically the same thing, with some syntactic sugar). Just add Manager (derived from Employee) and SalesPerson (derived from WorkerBee).
On old browsers, you may need a partial polyfill for Object.create, which looks like this:
if (!Object.create) {
Object.create = function(proto, props) {
if (typeof props !== "undefined") {
throw "The two-argument version of Object.create cannot be polyfilled.";
}
function ctor() { }
ctor.prototype = proto;
return new ctor();
};
}
There are transpilers for taking ES6 source with class and turning it into ES5 code. There's also my Lineage script, which makes inheritance a lot less verbose.
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();