2012-02-16 27 views
8

Tôi đang cố gắng sử dụng thừa kế trong JavaScript tại đây và tôi thấy vấn đề có giá trị mảng trong các lớp Parent được thừa hưởng bởi lớp Child. Dưới đây đang thừa kế bình thường:Thừa kế Javascript: Biến mảng của cha mẹ giữ lại giá trị

var Parent = function() { 
    this.list = []; 
}; 

var Child = function() {}; 

Child.prototype = new Parent; 
Child.prototype.constructor = Child; 

var obj1 = new Child; 

obj1.list.push("hello"); 

console.log(obj1.list); // prints ["hello"]; 

Khi tôi khởi tạo mới Child đối tượng (được thừa hưởng Chánh, trong đó có biến mảng tên danh sách) để obj1 và cố gắng đẩy obj1.list với một giá trị " hello ", obj1.list in [" hello "] .. cho đến nay rất tốt.

vấn đề này được đưa ra khi tôi đã làm ví dụ trên và tôi đã cố gắng để khởi tạo mới đối tượng Child để obj2 sau đó đẩy obj2 's danh sách với một giá trị 'tạm biệt', và obj2.list bây giờ in ["hello", "tạm biệt"]. (Xem mã dưới đây :)

var obj2 = new Child; 

obj2.list.push("goodbye"); 

console.log(obj2.list); // prints ["hello", "goodbye"]; 

tôi có thể có một quan niệm sai lầm ở đây, nhưng danh sách mảng trong Chánh được bằng cách nào đó giữ lại giá trị và tôi không biết tại sao.

Đây là một rắc rối lớn vì nếu tôi tái sử dụng các Chánh lớp với nhiều lớp trẻ khác như trường hợp trên, nếu Chánh có biến mảng chia sẻ đến các lớp học con của nó, giá trị sẽ được chia sẻ cho đứa trẻ khác các lớp học cũng là điều bất ngờ đối với tôi.

gì tôi mong đợi là, lớp trẻ em đại diện cho đối tượng mới, cùng đi vào Chánh lớp khi Child lớp được khởi tạo để obj1, mà sau đó khi tôi khởi tạo mới Child đối tượng để obj2, giá trị được đẩy từ obj1 không được chia sẻ với obj2.

- Câu hỏi -

bất cứ ai có thể giúp tôi tìm hiểu lý do tại sao danh sách (biến mảng của Chánh) trong ví dụ trên vẫn giữ được các giá trị/chia sẻ các giá trị được khởi xướng bởi Quỹ Trẻ em đối tượng (trong trường hợp trên, obj1obj2)?

Nếu bạn có một giải pháp khác có thể giải quyết vấn đề này, nó sẽ rất tốt đẹp cho bạn, nhưng tôi sẽ rất vui khi tìm ra vấn đề ở trên trước.

+0

Vì cả hai trường hợp đều tham chiếu cùng một ví dụ 'Parent' làm nguyên mẫu của chúng và do đó có cùng mảng. Đã có nhiều câu hỏi về JavaScript OOP ở đây, bạn nên xem chúng. –

+0

có thể trùng lặp của [Oes Undesrtanding OOP: các phiên bản sửa đổi các phiên bản khác] (http://stackoverflow.com/questions/4541788/undesrtanding-javascripts-oop-instances-modifying-other-instances) và [Thuộc tính mẫu thử nghiệm JavaScript không hoạt động như mong đợi với mảng và trường đối tượng] (http://stackoverflow.com/questions/8230085/javascript-prototype-property-not-working-as-expected-with-array-and-object-fiel) –

Trả lời

12

Khi đối tượng con có thuộc tính được thừa hưởng từ đối tượng nguyên mẫu, điều thực sự xảy ra là trẻ có tham chiếu cho mẫu thử nghiệm chứa thuộc tính. Đứa trẻ không có bản sao của nó.Vì vậy, cả hai trẻ em đang sử dụng cùng một mảng   — một đối tượng nguyên mẫu (một) Parent bạn đã gán cho Child.prototype.

Đầu tiên một số hình ảnh, sau đó thêm văn bản. :-)

new Parent() mang đến cho bạn điều này:

+-----------------+ 
| Parent instance | 
+-----------------+ 
| list = []  | 
+-----------------+

... mà bạn sau đó gán cho Child.prototype.

Sau đó, new Child() mang đến cho bạn điều này:

+------------------+ 
| Child instance 1 | 
+------------------+  +-----------------+ 
| (prototype)  |------->| Parent instance | 
+------------------+  +-----------------+ 
          | list = []  | 
          +-----------------+

Làm new Child() một lần nữa mang đến cho bạn một khác:

 
+------------------+  +------------------+ 
| Child instance 1 |  | Child instance 2 | 
+------------------+  +------------------+ 
| (prototype)  |---+ | (prototype)  |---+ 
+------------------+ | +------------------+ | 
         |       | 
         |       |  +-----------------+ 
         +-------------------------+---->| Parent instance | 
                 +-----------------+ 
                 | list = []  | 
                 +-----------------+

Như bạn có thể thấy, tất cả các Child trường hợp được chia sẻ cùng Parent instance (nguyên mẫu của chúng), có một mảng trên đó.

Khi bạn nói:

var obj = new Child(); 
obj.list.push("foo"); 

... đây là những gì engine JavaScript không khi nó thấy obj.list:

  1. Hình để xem nếu obj tài sản riêng của mình gọi là list. Nếu không, sau đó:
  2. Tìm hiểu xem nguyên mẫu của obj có thuộc tính riêng của mình là được gọi là list hay không. Nếu không, sau đó:
  3. Có vẻ để xem nguyên mẫu của nguyên mẫu obj có thuộc tính riêng của mình là được gọi là list hay không.
  4. Vv cho đến khi chúng tôi hết nguyên mẫu.

Trong trường hợp của bạn, vì obj không có riêng tài sảnlist của nó, động cơ trông để nguyên mẫu của nó (Parent dụ bạn đã gán cho Child.prototype), mà trong trường hợp này không có tài sản. Vì vậy, cái đó được sử dụng. Nó không được sao chép cho đứa trẻ hay bất cứ thứ gì, nó là được sử dụng. Và tất nhiên, vì nó là một mảng, việc đẩy thứ gì đó vào mảng thực sự đẩy nó lên mảng.

Nếu bạn đã assign cái gì đó để obj.list, sau đó obj sẽ nhận được riêng tài sảnlist của nó, phá vỡ chuỗi. Vì vậy, đặt this.list = []; vào Child sẽ cung cấp cho mỗi đứa trẻ danh sách riêng của mình.

Bạn sẽ thấy điều này bất kỳ lúc nào mẫu thử có tham chiếu đối tượng đối tượng trên đó, đối tượng là loại có thể được sửa đổi (đối tượng "có thể thay đổi"). Mảng, Ngày tháng, đối tượng đơn giản ({}), RegExps, v.v., tất cả đều có trạng thái và tất cả đều có thể được sửa đổi, vì vậy bạn sẽ thấy nó với chúng.(String trường hợp là không thay đổi, vì vậy mặc dù điều này tương tự xảy ra, bạn không thấy bất kỳ tác dụng từ nó bởi vì chuỗi không thể thay đổi.)

Với nguyên thủy, mặc dù bạn kế thừa chúng, bạn không thể thay đổi trạng thái của chúng (bạn chỉ có thể thay thế chúng bằng nguyên thủy mới có trạng thái khác), vì vậy bạn không thấy hiệu ứng tương tự này. Vì vậy, nếu obj kế thừa thuộc tính foo từ nguyên mẫu của nó và foo42, alert(obj.foo) sẽ nhận giá trị từ nguyên mẫu và hiển thị "42". Cách duy nhất để thay đổi foo là để nói obj.foo = 67 hoặc tương tự   — cung cấp objriêng của mình bản sao foo, khác với bản sao của mẫu thử nghiệm. (Điều này đúng ngay cả khi bạn sử dụng những thứ như ++--, ví dụ: ++obj.foo; thực sự được đánh giá là obj.foo = obj.foo + 1).

+5

Đồ họa ascii tuyệt vời của bạn đảm bảo nhiều hơn hơn 1, nhưng đó là tất cả tôi được phép thưởng –

+0

Yêu tôi một số nghệ thuật ASCII. ;-) –

+0

Đồng ý! 1 cho nghệ thuật ASCII. – pete

3

Điều bạn thiếu ở đây, là Parent.list đang được khởi tạo chỉ một lần; khi bạn sao chép 'nguyên mẫu của nó vào Child.prototype.

Lập tức một "phân lớp" trong JavaScript không không sẽ tự động gọi hàm tạo của cha mẹ. Vì vậy, tất cả các trường hợp của Child sẽ chia sẻ cùng một thể hiện của mảng.

Sửa đổi constructor của Child để gọi constructor cha mẹ nên là chữa bệnh mà bạn tìm kiếm cho bệnh của bạn:

var Child = function() { 
    Parent.call(this); 
}; 

Rõ ràng bạn đã có một số kiến ​​thức về đề tài này, nhưng nếu bạn quan tâm, đây là một vài văn bản về chủ đề này đáng đọc:

+0

Cảm ơn nikc! trung thực, mã mẫu của bạn là mã thực sự giải quyết vấn đề mới nhất của tôi trong giây lát này. Tôi quên sử dụng .call dẫn tôi đến câu hỏi này. –

+0

Cảm ơn, mã mẫu cũng cực kỳ hữu ích đối với tôi! –

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