2010-12-13 20 views
19

Có ai nhận thấy hành vi này trước đây không? Điều này thực sự đã ném tôi ra ... Tôi đã mong đợi các mảng được tạo kiểu mẫu là riêng tư cho mỗi cá thể lớp chứ không phải được chia sẻ giữa tất cả các cá thể lớp.Các thành viên đối tượng Javascript được tạo mẫu như mảng trở nên được chia sẻ bởi tất cả các phiên bản lớp

Ai đó có thể xác minh rằng đây là hành vi đúng và có thể giải thích hành vi này chi tiết hơn?

Lưu ý mã nhận xét và cách nó ảnh hưởng đến hành vi của tập lệnh.

<html> 
<head> 

<script type="text/javascript"> 

function print_r(title, object) { 

    var output = ''; 
    for(var key in object) { 

     output += key + ": " + object[ key ] + "\n"; 

    } 

    output = title + "\n\n" + output; 

    alert(output); 

} 

function Sandwich() { 

    // Uncomment this to fix the problem 
    //this.ingredients = []; 

} 

Sandwich.prototype = { 

    "ingredients" : [], 
    "addIngredients" : function(ingArray) { 

     for(var key in ingArray) { 

      this.addIngredient(ingArray[ key ]); 

     } 

    }, 
    "addIngredient" : function(thing) { 

     this.ingredients.push(thing); 

    } 

} 

var cheeseburger = new Sandwich(); 
cheeseburger.addIngredients([ "burger", "cheese" ]); 

var blt = new Sandwich(); 
blt.addIngredients([ "bacon", "lettuce", "tomato" ]); 

var spicy_chicken_sandwich = new Sandwich(); 
spicy_chicken_sandwich.addIngredients([ "spicy chicken pattie", "lettuce", "tomato", "honey dijon mayo", "love" ]); 

var onLoad = function() { 

    print_r("Cheeseburger contains:", cheeseburger.ingredients); 

}; 

</script> 

</head> 
<body onload="onLoad();"> 
</body> 
</html> 

Rất cám ơn.

Trả lời

31

Nguyên mẫu của một đối tượng chỉ là một đối tượng. Thuộc tính mẫu thử được chia sẻ giữa tất cả các đối tượng mà kế thừa từ đối tượng đó. Không có bản sao nào của các thuộc tính được tạo nếu bạn tạo một cá thể mới của một "lớp" (các lớp không tồn tại trong JS), tức là một đối tượng kế thừa từ nguyên mẫu.

Nó chỉ làm cho một sự khác biệt về cách bạn sử dụng những tài sản thừa kế:

function Foo() {} 

Foo.prototype = { 
    array: [], 
    func: function() {} 
} 

a = new Foo(); 
b = new Foo(); 

a.array.push('bar'); 
console.log(b.array); // prints ["bar"] 

b.func.bar = 'baz'; 
console.log(a.func.bar); // prints baz 

Trong tất cả các trường hợp, bạn luôn làm việc với cùng một đối tượng.

Nhưng nếu bạn gán một giá trị cho một thuộc tính của đối tượng, tài sản sẽ được thiết lập/tạo ra trên các đối tượng chính nó, chứ không phải nguyên mẫu của nó, và do đó không được chia sẻ:

console.log(a.hasOwnProperty('array')); // prints false 
console.log(a.array); // prints ["bar"] 
a.array = ['foo']; 
console.log(a.hasOwnProperty('array')); // prints true 
console.log(a.array); // prints ["foo"] 
console.log(b.array); // prints ["bar"] 

Nếu bạn muốn tạo mảng riêng cho mỗi trường hợp, bạn phải xác định nó trong các nhà xây dựng:

function Foo() { 
    this.array = []; 
} 

vì ở đây, this đề cập đến đối tượng new được tạo khi bạn gọi new Foo().

Nguyên tắc hàng đầu là: Instance dữ liệu cụ thể nên được gán cho dụ bên trong constructor, chia sẻ dữ liệu (như phương pháp) nên được gán cho nguyên mẫu.


Bạn có thể muốn đọc Details of the object model mô tả sự khác biệt giữa các lớp dựa trên ngôn ngữ dựa trên nguyên mẫu và cách đối tượng thực sự hoạt động.

Cập nhật:

Bạn có thể truy nguyên mẫu của một đối tượng qua Object.getPrototypeOf(obj) (có thể không hoạt động trong các trình duyệt rất cũ), và Object.getPrototypeOf(a) === Object.getPrototypeOf(b) mang đến cho bạn true. Nó là cùng một đối tượng, còn được gọi là Foo.prototype.

+0

Hmm ... Vâng, điều đó có ý nghĩa. Tôi đã mắc sai lầm khi mong đợi mẫu thử nghiệm hoạt động như một định nghĩa lớp. Điều khó hiểu là nếu Foo.prototype.intValue = 5, và bạn nói b.intValue = 4, a.intValue vẫn = 5. Vì vậy, nó chỉ thực sự áp dụng cho các thành viên của đối tượng/mảng của lớp. – Dan

+0

Sau đó, một lần nữa, nếu bạn nói b.array = ['whatever'], thì a.array cũng không thay đổi. Vì vậy, nhiệm vụ rõ ràng bằng cách sử dụng toán tử "=" ghi đè lên các thuộc tính prototyped trên một đối tượng được instanced. – Dan

+0

@Dan: Không, tôi cũng đã cho thấy trong mã của tôi rằng 'a.array = ['foo'];' sẽ không thay đổi nguyên mẫu. Nhưng trong trường hợp của bạn với số nguyên và trong trường hợp của tôi với mảng, bạn đang gán một giá trị hoàn toàn mới cho một thuộc tính. Và nếu bạn làm điều này, thuộc tính sẽ được đặt trên đối tượng thực tế. Nhưng nếu bạn làm 'a.array.push (1)', bạn không ** thiết lập thuộc tính, bạn chỉ gọi một phương thức trong mảng chứa trong 'a.array'. Bạn không thay đổi tham chiếu. –

1

Hành vi là chính xác. [] được chuyển đến new Array() trong thời gian chạy, nhưng chỉ một mảng như vậy được tạo.

Nói cách khác, Obj.prototype = {...} được thực hiện giống như bất kỳ sự xác nhận nào khác.

-1

Khi bạn làm var exp1 = new C(), JavaScript đặt exp1.[[Prototype]] = C.prototype. Khi bạn truy cập các thuộc tính của cá thể, JavaScript trước tiên kiểm tra xem chúng có tồn tại trực tiếp trên đối tượng đó hay không và nếu không, nó sẽ có dạng [[Prototype]]. Điều này có nghĩa rằng tất cả những thứ bạn định nghĩa trong nguyên mẫu được chia sẻ hiệu quả bởi tất cả các trường hợp, và thậm chí bạn có thể thay đổi các phần của nguyên mẫu và có các thay đổi xuất hiện trong tất cả các phiên bản hiện có.

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