Franck Freiburger
Franck Freiburger

Reputation: 28528

JavaScript constructor alternative

Since it is not possible to use new operator with an arguments array (.call or .apply), Then I wondering if I can do it by hand.

In the following code, are obj1 and obj2 equivalent (in ES3) ?

function MyClass(a, b, c) {}

var obj1 = new MyClass(2,3,4)

var tmp = function() {}
tmp.prototype = MyClass.prototype;
var obj2 = new tmp;
obj2.constructor = MyClass;
MyClass.call(obj2, 2,3,4);

- edit1 -

Does your answers mean that the above code is wrong or is not equivalent to an object construction ?

Upvotes: 5

Views: 2521

Answers (4)

KooiInc
KooiInc

Reputation: 122986

Using the following syntax, you should be able to apply/call a constructor directly without too much fuzz. Or instantiate it without using the new operator for that matter.

function MyClass(a,b,c){
  if (!(this instanceof MyClass)){
    return new MyClass(a,b,c)
  }
  // to demonstrate the arguments are usable using call/apply
  this.arg1 = a;
  this.arg2 = b;
  this.arg3 = c;
}

var obj2 = MyClass.call(null,1,2,3); // or MyClass.apply(null,[1,2,3])
alert([obj2.arg1,obj2.arg2,obj2.arg3].join(',')); //=> 1,2,3 

Upvotes: 1

Raynos
Raynos

Reputation: 169511

What about this :

var o = new MyClass;
MyClass.apply(o, args);

Your creating an object which inherits from the prototype properly through the new keyword then your calling the constructor again on that object with your parameters.

All this requires is that your constructor is well formed, doesn't crash on null parameters and doesn't have side effects when called more then once on the same object. Of course all these conditions are true because you write proper units, right?

To answer your actual question if you add

tmp.prototype.constructor = MyClass

then yes obj1 and obj2 are "the same".

Upvotes: 1

afshin
afshin

Reputation: 204

Well, you can use apply and get an object that is almost exactly like one instantiated with new like this:

function MyClass(a, b, c) {this.foo = a;}
var a, b = {}, args = [2, 3, 4]; // notice that b is an Object!
a = new MyClass(2,3,4);
MyClass.prototype.constructor.apply(b, [2, 3, 4]);
console.log('a.foo', a.foo, 'b.foo', b.foo);

As you'll see, those are equivalent, but if you check further:

console.log('b.constructor === a.constructor', b.constructor === a.constructor);

They don't have the same constructor. This is because b was originally an instance of "Object" since it was set to an object literal, and it was used as the context of the apply to the MyClass prototype's constructor. They will have the same attributes/methods, so maybe that's good enough for your needs?

EDIT: See @Raynos' comment below. This technique means you lose out on everything defined using the MyClass.prototype.

Upvotes: 0

Alnitak
Alnitak

Reputation: 339985

If you're using ES5 you can do this properly with Object.create():

function MyClass(a) {
    console.log('in constructor with param: ' + a);
    this.foo = a;
}

var a = new MyClass('a');

var b = Object.create(MyClass.prototype);
b.constructor.apply(b, ['b']);

console.log('a.foo = ', a.foo, ', b.foo = ', b.foo);
console.log(b instanceof MyClass);

output:

in constructor with param: a
in constructor with param: b
a.foo =  a , b.foo =  b
true

See http://jsfiddle.net/Qjfyt/1/, which shows the constructor only being called once, as expected.

Upvotes: 1

Related Questions