views:

183

answers:

2

I am doing Object Oriented programming in JavaScript without Prototype/jQuery (I use jQuery for other stuff). It has been working fine so far, but I ran into an issue with inheritance. Basically, when I declare objects in a constructor, they are shared between instances. Below is some sample code:

    A = function()
    {
        this.y = new Array();
    }

    A.prototype.doStuff = function(n)
    {
        this.y.push(n);
    }

    B = function()
    {
    }

    B.prototype = new A();

    var b1 = new B();
    var b2 = new B();

    b1.doStuff(100);
    b2.doStuff(200);

    console.log("b1:");
    console.log(b1);
    console.log("b2:");
    console.log(b2);

This outputs an array [100, 200] for both b1 and b2. What I want is for b1 and b2 to have their own, separate arrays for y. How do I go about this?

(PS. I assume that Prototype's class system has something built in for this. However I would rather not rewrite a bunch of my code to use that class system)

+1  A: 
B.prototype = new A();

this creates only one instance of A for every B.

You might consider doing sth like

B = function()
{
  this.$super = new A();
}


B.prototype.doStuff = function()
{
  return this.$super(args);
}


C = function ()
{
  this.$super = new B();
}


C.prototype.doStuff = function()
{
  return this.$super(args);
}

C.prototype.whoIsMyGrandParent = function()
{
  return this.$super.$super;
}

Javascript has NO real inheritation

Ghommey
That suffers from what I call the "grandchild" problem -- as soon as there's a `C` that derives from `B`, it won't work anymore, because there's no way for `B` to refer to `A`. (Also, FWIW, `super` is a reserved word in JavaScript.)
T.J. Crowder
sorry that is right. I used $super in my scripts.I don't see your grandchild problem.
Ghommey
Javascript has inheritance. It's just not classical inheritance. Prototypal OO is an entirely different, but entirely legitimate, form of OO.
eyelidlessness
JavaScript has prototype-base inheritance, not no inheritance.
Tim Down
@ghommey: Re the GrandChild problem: You can't use a single `$super` property on the instance to refer to the superclass, because if you have Parent<-Child<-GrandChild and create a GrandChild instance, `this.$super` will always point to `Child` (even when `Child` tries to use it to refer to `Parent`). This means `Child` cannot reference `Parent`, which is necessary for supercalls to work correctly throughout the tree. Instance properties can only be used if they have unique names (usually based on the name of the superclass), at which point it gets messy very quickly indeed.
T.J. Crowder
--- Time Down --- Thank you!!! So my eyes are getting sore looking at these examples! Yes, JS inheritance is challenging to grok. That's why I TDD'd it and wrote an essay on it. I suggest you steal my code and rename the examples. I've got tests and implementations here: http://github.com/roblevintennis/Testing-and-Debugging-JavaScript Cheers and have fun playing with the code!
Rob
+2  A: 

The problem is that your A constructor function is only run once for all B instances, and so this.y is a reference to just one array. Any reference to it from B will be resolved via the prototype chain to the single, central A reference, which only has one y. This is a very useful feature, just not for what you're trying to do here.

The answer is to separate construction from initialization; the constructor sets up central resources and the initializer initializes an instance. This is how the issue is handled by most of the "class" implementations I've seen for JavaScript. For that to work, you have to provide a means for each level in the hierarchy to call the previous level's initializer (which is useful for other methods as well) -- e.g., supercalls.

Supercalls are why you're probably better off using something like Prototype for this: They're hard to do well, and it's very easy to fall into the "grandchild" problem -- a solution that seems to work, but ends up only working with a Parent<-Child structure, not a Parent<-Child<-GrandChild structure.

However, if you're going to do your own inheritance mechanism, this post on supercalls from my pathetically-anemic blog may be helpful as I go into some of those issues in depth. It takes a simplified version of Prototype's inheritance mechanism, deconstructs it a bit, and talks about a way to do supercalls that doesn't have some issues I have with Prototype's current supercalls. That may help you with doing them in your own mechanism.

T.J. Crowder
Yea--I ended up doing something like what you described in your blog post. Basically, I have a separate `construct(target)` function that initializes instance-specific variables for `target`. Children call this on their super class. Not particularly pretty, but it seems to work.
Sam Lee