Skip to content Skip to sidebar Skip to footer

Javascript Prototype Inheritance

This seems inconsistent, but probably is due to the fact that I'm new to javascript's prototype inheritance feature. Basically, I have two base class properties, 'list' and 'name

Solution 1:

The thing with prototypes, is that you can do reads all day long from them, and it wont change the underlying structure of what is pointing to what. However, the first time you do an assignment, you are replacing, on that instance, what that property points to.

In your case, you didn't actually reassign the prototyped property, you modifed the value of the underlying structure that was found at in that property.

What this means is that all objects that share a prototype of A actually share the implementation of A. That means any state carried in A will be found on all instances of B. The moment you do an assignment to that property on an instance of B, you have effectively replaced what that instance (and only that instance) points to (I believe this is due to the fact there is a new property "name" on B that gets hit in the scope chain, before it reaches the "name" implementation on A).

EDIT:

To give a more explicit example of whats going on:

B.prototype = newA();
var obj1 = newB();

Now, the first time we do a read, the intepreter does something like this:

obj1.name;

Interpreter: "I need property name. First, check B. B does not have 'name', so lets continue down the scope chain. Next up, A. Aha! A has 'name', return that"

However, when you do a write, the interpreter doesn't actually care about the inherited property.

obj1.name = "Fred";

Interpreter: "I need to assign to property 'name'. I'm within the scope of this instance of B, so assign 'Fred' to B. But I leave everything else farther down the scope chain alone (i.e., A)"

Now, next time you do a read...

obj1.name;

Interpreter: "I need property 'name'. Aha! this instance of B has the property 'name' already sitting on it. Just return that - I don't care about the rest of the scope chain (i.e. A.name)"

So, the first time you write to name, it inserts it as a first class property on the instance, and doesn't care anymore about what is on A. A.name is still there, it's just further down the scope chain, and the JS interpreter doesn't get that far before it found what it was looking for.

If "name" had a default value in A (as you have, which is "A"), then you would see this behaviour:

B.prototype = newA();
var obj1 = newB();
var obj2 = newB();

obj1.name = "Fred";

alert(obj1.name); // Alerts Fredalert(obj2.name); // Alerts "A", your original value of A.name

Now, in the case of your array, you never actually replaced list on the scope chain with a new array. What you did is grab a handle on the array itself, and added an element to it. Hence, all instances of B are affected.

Interpreter: "I need to get the 'list' propery, and add an element to it. Checking this instance of B ... nope, does not have a 'list' property. Next in the scope chain: A. Yep, A has 'list', use that. Now, push onto that list"

This would not be the case if you did this:

obj1.list = [];
obj1.push(123);

Solution 2:

  • You're saying in this code B is an implementation of A.
  • Then you're saying obj1 is a new instance of B and so should grab all of A's values.
  • Then you add an element to the referenced location of the list in obj2 which in turn adds an element to the referenced location of the list in A (because they are referencing the same thing).
  • Then you change the value of the name in obj1.
  • Then you're saying obj2 is a NEW instance of B and so should grab all of A's values.

You changed the values of the list referenced in A but not the reference itself. obj2.name should be "A".

Solution 3:

Of course, I would prefer that any subsequent subclass instances not get values from other instances, but if that's going to happen, it should be consistent!

Then don't inherit from a new instance of A, inherit from A's prototype. This will ensure that you don't inherit state, you only inherit behaviour. That's the tricky part with prototypal inheritance, it inherits state too, not only behaviour as in classic OOP inheritance. In JavaScript, constructor functions should only be used for setting up the state of the instance.

var A = function (list) {
    this.list = list;
    this.name = "A";
};

A.prototype.getName = function () {
    returnthis.name;
};

var B = function (list) {
    this.list = list;
    this.name = "B";
};

B.prototype = A.prototype;   // Inherit from A's prototype
B.prototype.constructor = B; // Restore constructor objectvar b = newB([1, 2, 3]);

// getName() is inherited from A's prototypeprint(b.getName()); // B

And by the way, if you want to change something in A, through B, you have to do this:

B.prototype.name = "obj1";

Solution 4:

You are re-assigning the name variable. What is happening is that you are re-assigning a new name variable to obj1. Your declaration of name is overriding the declaration on the prototype object(so you don't see it). With the list, you are mutating the list in place not changing it's reference.

Solution 5:

This behavior happens because you're not assigning a value to "list", you're modifying it.

This is perfectly sensible behavior when you realize how the prototype chain works. When you reference obj1.list, it first looks if "list" exists in obj1, uses that if found, and otherwise uses the one found on obj1.prototype (which is MyClass.prototype.list).

So:

obj1.list.push("test"); // modifies MyClass.prototype.list
obj1.list = ["new"]; // creates a "list" property on obj1
obj1.list.push("test"); // modifies obj1.list, not MyClass.prototype.list
delete obj1.list; // removes the "list" property from obj1
// after the delete, obj1.list will point to the prototype again
obj1.list.push("test"); // again modifies MyClass.prototype.list

The most important take-away is this: "prototypes are not classes". Prototypes can do a reasonably good job of faking classes, but they are not classes, so you should not treat them as such.

Post a Comment for "Javascript Prototype Inheritance"