2009-10-07 119 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