1

Is it possible to change a constructor so that some extra action is run after an object is created. I tried something like:

var origFoo = Foo
Foo = function() {
    origFoo.apply(this, arguments);
    /* extra actions */
}
Foo.prototype = new origFoo();

but this has several problems like the constructor being run twice or changing the prototype chain.

Artefacto
  • 96,375
  • 17
  • 202
  • 225

1 Answers1

3

You are very close. You should assign the Foo.prototype to the origFoo.prototype in order to get the same prototype chain. Everything else is spot on!

Example:

var Foo = function () {
    console.log('OriginalFoo');
};

Foo.prototype.method1 = function () {
    console.log('Method1');
};

OriginalFoo = Foo;

Foo = function () {
    OriginalFoo.apply(this, arguments);
    console.log('NewFoo');
};

Foo.prototype = OriginalFoo.prototype;

Foo.prototype.method2 = function () {
    console.log('Method2');
};

var x = new Foo();

x.method1();
x.method2();

Demo: http://jsbin.com/ibatah/1/edit?js,console,output

PS: There still is the problem of static-like properties (Foo.prop), but i'm afraid i don't have a solution for that other than copying them one at a time.

EDIT: Solution for special constructors.

Indeed there are constructors which don't like to be called as functions ex: Image. To get over it, you can do the more awkard solution below. You take advantage of the fact that you can return an object from the constructor and it takes the place of the one created with new. In the overridden constructor you must always use this new object when calling methods instead of this.

var Foo = function(a,b,c) {
  console.log('OriginalFoo',a,b,c);
};
Foo.prototype.prop1 = 'Property1';
Foo.prototype.method1 = function() {
  console.log('Method1', this.prop1);
};


OriginalFoo = Foo;
Foo = function(a,b,c) {
  var obj = new OriginalFoo(a,b,c);

  obj.init('Changed...'); // or this.init.call(obj,'Changed!'); 
  this.init('Not Changed'); // applies to a discarded object, has no effect

  console.log('NewFoo');
  return obj;
};

Foo.prototype = OriginalFoo.prototype;

Foo.prototype.prop2 = 'Property2';

Foo.prototype.method2 = function() {
  console.log('Method2', this.prop2);
};
Foo.prototype.init = function(param) {
  this.prop2 = param;
};



var x = new Foo('1','2','3');
console.log(x.prop1);
console.log(x.prop2);
x.method1();
x.method2();

Demo: http://jsbin.com/ibatah/2/edit?js,console,output

Tibos
  • 27,507
  • 4
  • 50
  • 64
  • I've edited the code from your JSBin into your answer to avoid link rot and make a more complete answer - please consider doing this in the future as it helps others find solutions more easily. Other than that, +1 `:)` – Bojangles Sep 27 '13 at 10:38
  • @Bojangles I didn't post the full code because i consider it laregely irrelevant. The OP has almost correct code and i described the only change he needed to do to make it perfect. Anyway, thanks for the help. – Tibos Sep 27 '13 at 10:41
  • StackOverflow answers should be helpful to others in the future, too. Visitors will often skip the question and look for a complete solution in the answer, so it's good to present one in the form of your posted code – Bojangles Sep 27 '13 at 10:45
  • 1
    Interestingly this doesn't seem to work with all types of classes. For instance with `XMLHttpRequest` I get an error saying `apply` doesn't exist. If I then bind `Function.prototype.apply` to `XMLHttpRequest` and try to call that, I get `TypeError: DOM object constructor cannot be called as a function`. Any suggestions here? – Artefacto Sep 27 '13 at 11:53