2009-10-07 196 views
16

这似乎不一致,但可能是由于我是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);  

回答

4
  • 你是说在这段代码B是 实现A的
  • 然后你说OBJ1是B的新 实例所以应该抓住A的所有价值 。
  • 然后你在obj2的 这反过来又增加了一个元素以A 列表的引用 位置上的元素添加到列表中的 引用的位置(因为它们引用的是同样的事情)。
  • 然后在obj1中更改名称 的值。
  • 然后你说obj2是B的一个新的 实例,所以应该抓住A的值的所有 。

您更改了A中引用的列表的值,但未更改引用本身。 obj2.name应该是“A”。

3

当你将一个新对象推入obj1.list时,你正在改变原型上的现有列表。当您更改名称时,您正在为obj1分配一个属性,实例不是的原型。请注意,如果您完成了相同的事情:

obj1.list = ["from obj1"] 
... 
console.log(obj2.list) // <--- Will log [] to console 
2

您正在重新分配名称变量。发生了什么是你正在重新分配一个新的名称变量obj1。您的名称声明覆盖原型对象上的声明(所以您没有看到它)。通过列表,您可以在不改变参考的情况下对列表进行变更。

13

与原型的东西,是你可以从他们整天读取,它不会改变什么是指向什么的底层结构。但是,你第一次做任务时,你正在替换那个实例,那个属性指的是什么。

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

这意味着所有共享A原型的对象实际上共享共享这意味着A中携带的任何状态都将在B的所有实例上找到。对B实例的属性,你已经有效地取代了那个实例(只有那个实例)指向的东西(我相信这是由于事实上B上有一个新的属性“name”,它在范围链中被击中,之前它达到了A)上的“名称”实现。

编辑:

举个怎么回事了更明显的例子:

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

现在,我们第一次做阅读时,intepreter确实是这样的:

obj1.name; 

解释:“!我需要的属性名称首先,检查B. B没有‘名’,所以让我们继续往下作用域链接下来,A阿哈A有‘名’,返回”

但是,当你写,解释并不真正关心的继承财产。

obj1.name = "Fred"; 

解释:“我需要分配财产的名字“我是这个实例B的的范围之内,所以分配‘弗雷德’,以B.但我离开一切更远下来。单独作用域链(即A)“

现在,你读下一次......

obj1.name; 

解释:”!我需要的属性‘名’啊哈这实例B的有该物业的名称已经坐在它上面。刚刚返回 - 我不关心范围链的其余部分(即A.name)”

所以,你写的名字第一次时,它把它作为实例一流酒店,和不再关心AAname是什么还有,它只是在范围链的更远处,而JS解释器在找到它所要找的东西之前还没有达到那么远

如果“命名”曾在一个默认值(你有,这是‘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 

现在,在CAS您的阵列的E,你从来没有真正更换了一个新的阵列作用域链名单。你所做的是在数组本身上抓取一个句柄,并为其添加一个元素。因此,B的所有实例都受到影响。

解释器:“我需要获取'list'propery,然后添加一个元素,检查这个B ... nope的实例,没有'list'属性。 。是的,一个有“清单”,使用现在,推入该名单”

是,如果你没有这样的情况:

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

谢谢大家的解释。所以,即使它说: B.prototype = new A(); 和 obj2 = new B(); A()的“list”属性没有重新创建,它只能被回收? 无论如何,我可以接受这个命运:)那么,有一个包含数组的基类的最佳方式是什么?我正在构建一棵树,需要一个通用的带子节点类。 – med 2009-10-07 22:11:00

+1

非常好的解释。 – 2010-12-08 20:59:36

0

发生此行为是因为你不给“列表”赋值,你正在修改它。

这是非常明智的行为,当你意识到如何原型链的作品。当您引用obj1.list,它首先,如果“清单”中OBJ1存在,使用,如果发现了,否则使用一个在obj1.prototype发现(这是MyClass.prototype.list)。

所以:

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 

最重要的外卖是这样的: “原型是不是类”。原型可以在伪造类的方面做得相当好,但它们不是类,所以你不应该这样对待它们。

4

当然,我宁愿任何 随后的子类的实例没有得到其他实例 值,但如果 这会发生,它应该是一致的 !

然后不要从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"; 
+0

非常好的信息,谢谢。 – 2010-12-08 21:02:43