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". I instantiate two subclasses and give values to the properties. When I instantiate the second subclass, it gets the list values from the first subclass instance, but only for the "list" and not for "name". What's going on?? 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!

Here is a code snippet:

    function A()
    {
        this.list = [];
        this.name = "A";
    }
    function B()
    {
    }
    B.prototype = new A();

    var obj1 = new B();
    obj1.list.push("From obj1");
    obj1.name = "obj1";

    var obj2 = new B();

    //output:
    //Obj2 list has 1 items and name is A
    //so, the name property of A did not get replicated, but the list did
    alert("Obj2 list has " + obj2.list.length + " items and name is "+ obj2.name);    

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 = new A();
var obj1 = new B();

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 (ie, 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 (ie 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 AAname 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 = new A();
var obj1 = new B();
var obj2 = new B();

obj1.name = "Fred";

alert(obj1.name); // Alerts Fred
alert(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);

  • 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".


    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 () {
        return this.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 object
    
    var b = new B([1, 2, 3]);
    
    // getName() is inherited from A's prototype
    print(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";
    
    链接地址: http://www.djcxy.com/p/30054.html

    上一篇: 将Javascript getters / setters复制到另一个原型对象

    下一篇: JavaScript原型继承