1

In trying to port a Java app to JavaScript, I'm attempting the following inheritance technique:

var grandchild = new Grandchild();

Function.prototype.extend =
    function(parent) {
        var parentPrototype = parent.prototype;
        this.prototype = Object.create(parentPrototype);
        this.prototype.superPrototype = parentPrototype;
        this.prototype.superConstructor = parent;
        this.prototype.constructor = this;
    }
;

function Parent(){
    this.doStuff();
}

Parent.prototype.doStuff =
    function() {
    }
;

Child.extend(Parent);
function Child(){
    this.superConstructor.call(this);
}

Child.prototype.doStuff = function() {
    this.superPrototype.doStuff.call(this);
}

Grandchild.extend(Child);
function Grandchild(){
    this.superConstructor.call(this);
}

Grandchild.prototype.doStuff = function() {
    this.superPrototype.doStuff.call(this);
}

It works to one level of inheritance (i.e. var child = new Child()) but with new Grandchild() it throws an Uncaught RangeError: Maximum call stack size exceeded because it gets stuck in infinite recursion on function Child().

  1. What exactly is happening and why?
  2. How can I tweak this technique so it allows me to call this.superConstructor.call(this) and this.superPrototype.doStuff.call(this) as shown without hitting the infinite recursion on the immediate parent?

It works when I specify the super class in the calls as follows, but I would prefer not to have to do this:

function Child(){
    Parent.call(this);
}

Child.prototype.doStuff = function() {
    Parent.prototype.doStuff.call(this);
}

function Grandchild(){
    Child.call(this);
}

Grandchild.prototype.doStuff = function() {
    Child.prototype.doStuff.call(this);
}
Navigateur
  • 1,852
  • 3
  • 23
  • 39
  • 3
    In the `Child` function, `this.superConstructor` isn't `Child`'s `superConstructor` when `Child` is called from `Grandchild`. Unfortunately, I am too sleep-deprived to provide a solution. – user2357112 Mar 23 '15 at 11:16
  • No, you cannot use `.superConstructor` or `.superPrototype`, it just doesn't work with them. Scrap them and use static references. – Bergi Mar 23 '15 at 11:24
  • 1
    See also [this](http://stackoverflow.com/a/24700014/1048572) and [that](http://stackoverflow.com/q/20463145/1048572) – Bergi Mar 23 '15 at 11:33

1 Answers1

1

Okay. Here's the thing: In JS, this and new, together, are the single most confusing concept to grasp.

When you call new GrandChild(), what's this?

this is an object with its __proto__ set to the object pointed to by GrandChild.prototype. We will call this version of this, gc_this.

GrandChild() now executes this.superConstructor.call(this);. We can also say that it executes gc_this.superConstructor.call(gc_this);, because we theoretically renamed this within Grandchild() to gc_this.

The above execution calls Child() and has the same effect of executing new Child(). The only difference is that since we passed gc_this to it, it won't return anything and any and all this within Child() have been replaced with gc_this.

Okay, so far so good. No problems yet.

But now starts the problem: Since all this within Child() have been replaced with gc_this, the code within Child() has changed to:

function Child(){
    gc_this.superConstructor.call(gc_this);
}

This is the same exact line that we saw when we renamed this within GrandChild() to gc_this.

Therefore, this ends up in an infinite recursion.

And the only way to implement multi-level classical-inheritance in JS is to directly use the static class names, rather than relying on dynamically set custom properties.


If you want to gain some more insight on the topic, I recommend you have a look at: http://www.javascripttutorial.net/javascript-prototype/

zhirzh
  • 3,273
  • 3
  • 25
  • 30