JavaScript原型继承

这看起来不一致,但可能是由于我是JavaScript的原型继承功能新手。

基本上,我有两个基类属性,“列表”和“名称”。 我实例化两个子类并给这些属性赋值。 当我实例化第二个子类时,它从第一个子类实例中获取列表值,但只针对“列表”而不针对“名称”。 这是怎么回事?? 当然,我宁愿任何后续的子类实例都不会从其他实例获取值,但如果发生这种情况,它应该是一致的!

这是一段代码片段:

    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);    

原型的东西是,你可以从它们整天读取,而且它不会改变指向什么的底层结构。 但是,第一次执行任务时,您将替换该属性指向的内容。

在你的情况下,你实际上并没有重新分配原型属性,而是修改了在该属性中找到的基础结构的值。

这意味着共享A原型的所有对象实际上共享A的实现。这意味着A中携带的任何状态都可以在B的所有实例上找到。当您在B实例上对该属性进行赋值时,你已经有效地取代了那个实例(只有那个实例)指向的内容(我相信这是由于B上有一个新的属性“name”,它在范围链中被命中,在到达“name”之前,在A上实施)。

编辑:

给出一个更直观的例子:

B.prototype = new A();
var obj1 = new B();

现在,我们第一次阅读时,这个解释器就是这样做的:

obj1.name;

解释者:“我需要属性名称,首先,检查B. B没有'名称',所以让我们继续沿着范围链继续下一步,A. Aha!A有'name',返回”

但是,当您执行写操作时,解释器实际上并不关心继承的属性。

obj1.name = "Fred";

解释者:“我需要分配属性'名',我在B的这个实例的范围内,所以给'B'分配'Fred',但是我把所有其他东西放在范围链中(即A)”

现在,下次你阅读...

obj1.name;

解释器:“我需要属性'name',Aha!这个B的实例已经拥有属性'name',只需返回 - 我不关心作用域链的其余部分(即A.name)”

所以,第一次写名字时,它将它作为实例的第一类属性插入,并且不再关心AAname上的内容,它只是在作用域链的更远处,而JS解释器不会在它发现它所寻找的东西之前没有那么远。

如果“名称”在A中有一个默认值(就像你有,这是“A”),那么你会看到这样的行为:

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

现在,就你的数组而言,你从来没有用一个新的数组实际替换范围链上的列表。 你所做的是在数组本身上抓取一个句柄,并为其添加一个元素。 因此,B的所有实例都受到影响。

解释器:“我需要得到'list'propery,然后添加一个元素,检查这个B ... nope的实例,没有'list'属性,接下来在范围链中:A.Yep,A有'列表',使用它。现在,推入该列表“

如果你这样做,情况就不会如此:

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

  • 你在这个代码中说B是A的一个实现。
  • 然后你说obj1是B的一个新实例,所以应该抓住A的所有值。
  • 然后,您将元素添加到obj2中列表的引用位置,然后将元素添加到A中列表的引用位置(因为它们引用的是相同的东西)。
  • 然后你改变obj1中的名字的值。
  • 然后你说obj2是B的一个新实例,所以应该抓住A的所有值。
  • 您更改了A中引用的列表的值,但未更改引用本身。 obj2.name应该是“A”。


    当然,我宁愿任何后续的子类实例都不会从其他实例获取值,但如果发生这种情况,它应该是一致的!

    那么不要从A的新实例继承,从A的原型继承。 这将确保你不会继承状态,你只能继承行为。 这是原型继承的棘手部分,它也继承了状态,不仅像传统的OOP继承一样行为。 在JavaScript中,构造函数只能用于设置实例的状态。

    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
    

    顺便说一句,如果你想改变A中的某些东西,通过B,你必须这样做:

    B.prototype.name = "obj1";
    
    链接地址: http://www.djcxy.com/p/30053.html

    上一篇: javascript prototype inheritance

    下一篇: Understanding prototypal inheritance in JavaScript