2009-10-07 48 views
16

Điều này có vẻ không nhất quán, nhưng có thể là do thực tế tôi mới sử dụng tính năng kế thừa nguyên mẫu của javascript.thừa kế nguyên mẫu javascript

Về cơ bản, tôi có hai thuộc tính cơ sở, "danh sách" và "tên". Tôi khởi tạo hai lớp con và cung cấp các giá trị cho các thuộc tính. Khi tôi khởi tạo lớp con thứ hai, nó sẽ nhận các giá trị danh sách từ cá thể lớp con đầu tiên, nhưng chỉ cho "danh sách" chứ không phải cho "tên". Có chuyện gì vậy ?? Tất nhiên, tôi muốn rằng bất kỳ trường hợp lớp con tiếp theo nào không nhận được các giá trị từ các cá thể khác, nhưng nếu điều đó xảy ra, nó phải nhất quán!

Đây là một đoạn mã:

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

Trả lời

4
  • Bạn đang nói trong mã này B là một thực hiện của A.
  • Sau đó, bạn đang nói obj1 là một dụ mới của B và nên lấy tất cả giá trị của A.
  • Sau đó, bạn thêm một yếu tố đến vị trí tham chiếu của danh sách trong obj2 mà lần lượt thêm phần tử vào vị trí tham chiếu của danh sách trong Một (vì họ đang tham chiếu điều tương tự).
  • Sau đó, bạn thay đổi giá trị của tên trong obj1.
  • Sau đó, bạn đang nói obj2 là trường hợp mới của của B và vì vậy, hãy lấy tất cả giá trị của A.

Bạn đã thay đổi giá trị của danh sách được tham chiếu trong A chứ không phải chính tham chiếu. obj2.name phải là "A".

3

Khi bạn đẩy một đối tượng mới vào obj1.list bạn đang tắt danh sách hiện có trên nguyên mẫu. Khi bạn thay đổi tên, bạn chỉ định thuộc tính trên obj1, ví dụ, không phải là nguyên mẫu. Lưu ý điều tương tự sẽ xảy ra nếu bạn đã thực hiện:

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

Bạn đang gán lại biến tên. Điều đang xảy ra là bạn đang gán lại một biến tên mới cho obj1. Khai báo tên của bạn là ghi đè khai báo trên đối tượng nguyên mẫu (vì vậy bạn không thấy nó). Với danh sách, bạn đang thay đổi danh sách tại chỗ không thay đổi tham chiếu của nó.

13

Điều với nguyên mẫu, là bạn có thể đọc tất cả các ngày dài từ họ, và nó sẽ không thay đổi cấu trúc cơ bản của những gì đang trỏ đến những gì. Tuy nhiên, lần đầu tiên bạn thực hiện một bài tập, bạn sẽ thay thế, trên ví dụ, thuộc tính đó trỏ đến điều gì.

Trong trường hợp của bạn, bạn đã không thực sự gán lại thuộc tính prototyped, bạn đã điều chỉnh giá trị của cấu trúc bên dưới được tìm thấy tại thuộc tính đó.

Điều này có nghĩa là tất cả các đối tượng chia sẻ nguyên mẫu A thực tế chia sẻ việc thực hiện A. Điều đó có nghĩa là bất kỳ trạng thái nào được thực hiện trong A sẽ được tìm thấy trên tất cả các trường hợp B. Thời điểm bạn thực hiện chuyển nhượng tài sản trên một thể hiện của B, bạn đã thay thế hiệu quả trường hợp đó (và chỉ có cá thể đó) trỏ tới (tôi tin rằng điều này là do thực tế có một tên "thuộc tính" mới trên B bị tấn công trong chuỗi phạm vi, trước đây nó đạt đến việc thực hiện "tên" trên A).

EDIT:

Để cung cấp một ví dụ rõ ràng hơn của whats going on:

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

Bây giờ, lần đầu tiên chúng tôi làm một đọc, intepreter làm điều gì đó như thế này:

obj1.name; 

Thông dịch viên: "Tôi cần tên tài sản. Đầu tiên, hãy kiểm tra B. B không có 'tên', vì vậy hãy tiếp tục xuống chuỗi phạm vi. Tiếp theo, A. Aha! A có 'tên', trả về"

Tuy nhiên, khi bạn viết, trình thông dịch không thực sự quan tâm đến thuộc tính được kế thừa.

obj1.name = "Fred"; 

Interpreter: "Tôi cần phải gán cho 'tên' tài sản tôi trong phạm vi trường hợp này của B, vì vậy gán 'Fred' đến B. Nhưng tôi rời khỏi mọi thứ khác xa hơn xuống. chuỗi phạm vi một mình (ví dụ: A) "

Bây giờ, lần sau khi bạn làm một đọc ...

obj1.name; 

Interpreter:". tôi cần 'tên' tài sản Aha trường hợp này của B có tên 'tài sản' đã ngồi trên đó. Chỉ cần trả lại - Tôi không quan tâm đến phần còn lại của chuỗi phạm vi (tức là A.name) "

Vì vậy, lần đầu tiên bạn viết tên, nó chèn nó làm thuộc tính hạng nhất trên cá thể và không quan tâm nữa về những gì trên AAname là vẫn còn ở đó, nó chỉ tiếp tục xuống chuỗi phạm vi và thông dịch viên JS không nhận được điều đó xa trước khi nó tìm thấy những gì nó đang tìm kiếm.

Nếu " tên "có giá trị mặc định trong A (như bạn có, là" A "), sau đó bạn sẽ thấy hành vi này:

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ây giờ, trong cas e của mảng của bạn, bạn không bao giờ thực sự thay thế danh sách trên chuỗi phạm vi bằng một mảng mới. Những gì bạn đã làm là lấy một handle trên chính mảng đó và thêm một phần tử vào nó. Do đó, tất cả các trường hợp của B bị ảnh hưởng.

Thông dịch viên: "Tôi cần phải có được danh sách 'phù hợp' và thêm yếu tố vào đó. Kiểm tra trường hợp này của ... không, không có thuộc tính 'danh sách'. Tiếp theo trong chuỗi phạm vi: A . Yep, A có 'danh sách', sử dụng Bây giờ, đẩy lên danh sách đó"

này sẽ không là trường hợp nếu bạn đã làm điều này:.

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

Cảm ơn tất cả các bạn cho lời giải thích. Vì vậy, mặc dù nó nói: B.prototype = new A(); và obj2 = new B(); Thuộc tính "danh sách" của A() không được tạo lại, nó chỉ được tái chế? Dù sao, tôi có thể chấp nhận số phận này :) Vì vậy, cách tốt nhất để có một lớp cơ sở có chứa một mảng là gì? Tôi đang xây dựng một cây và cần một lớp nút chung với trẻ em. – med

+1

Giải thích rất tốt. –

0

hành vi này xảy ra bởi vì bạn không chỉ định giá trị cho "danh sách", bạn đang sửa đổi nó.

Đây là hành vi hoàn toàn hợp lý khi bạn nhận ra cách chuỗi nguyên mẫu hoạt động. Khi bạn tham khảo obj1.list, đầu tiên nó sẽ xuất hiện nếu "danh sách" tồn tại trong obj1, sử dụng nếu được tìm thấy, và nếu không thì sử dụng cái được tìm thấy trên obj1.prototype (đó là MyClass.prototype.list).

Vì vậy:

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 

Điều quan trọng nhất take-away là thế này: "nguyên mẫu không phải là lớp học". Prototypes có thể làm một công việc hợp lý của các lớp giả mạo, nhưng chúng không phải là các lớp, vì vậy bạn không nên đối xử với chúng như vậy.

4

Tất nhiên, tôi muốn rằng bất kỳ trường lớp con sau này không nhận được giá trị từ các trường hợp khác, nhưng nếu rằng sẽ xảy ra, nó phải là phù hợp!

Sau đó, không kế thừa từ phiên bản mới A, kế thừa từ nguyên mẫu của A. Điều này sẽ đảm bảo rằng bạn không kế thừa trạng thái, bạn chỉ kế thừa hành vi. Đó là phần khó khăn với sự thừa kế nguyên mẫu, nó cũng thừa kế trạng thái, không chỉ hành vi như trong thừa kế OOP cổ điển. Trong JavaScript, hàm xây dựng chỉ nên được sử dụng để thiết lập trạng thái của cá thể.

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 

Và bằng cách này, nếu bạn muốn thay đổi điều gì đó trong A, qua B, bạn phải làm điều này:

B.prototype.name = "obj1"; 
+0

Thông tin tuyệt vời, cảm ơn. –

Các vấn đề liên quan