Объекты объектов Javascript, которые прототипируются как массивы, становятся доступными для всех экземпляров класса
Кто-нибудь заметил это поведение раньше? Это действительно отбросило меня... Я бы ожидал, что прототипированные массивы будут закрыты для каждого экземпляра класса, а не разделены между всеми экземплярами класса.
Может ли кто-нибудь убедиться, что это правильное поведение и, возможно, более подробно объясняет это поведение?
Обратите внимание на комментированный код и то, как он влияет на поведение script.
<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>
Большое спасибо.
Ответы
Ответ 1
Прототипом объекта является только объект. Свойства прототипа разделяются между всеми объектами, которые наследуются от этого объекта. Никакие копии свойств не создаются, если вы создаете новый экземпляр класса (классы в любом случае не существуют в JS), то есть объект, который наследуется от прототипа.
Это только влияет на то, как вы используете эти унаследованные свойства:
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
Во всех этих случаях вы всегда работаете с одним и тем же объектом.
Но если вы присвоите значение свойству объекта, свойство будет установлено/создано на самом объекте, а не на его прототипе, и, следовательно, не разделяется:
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"]
Если вы хотите создать собственные массивы для каждого экземпляра, вы должны определить его в конструкторе:
function Foo() {
this.array = [];
}
потому что здесь this
относится к объекту new
, который генерируется при вызове new Foo()
.
Эмпирическое правило: Экземпляр -специфические данные должны быть назначены экземпляру внутри конструктора, для прототипа должны быть назначены данные shared (например, методы).
Возможно, вы захотите прочитать Сведения об объектной модели, в которой описываются различия между классами и прототипами, и как работают объекты.
Update:
Вы можете получить доступ к прототипу объекта через Object.getPrototypeOf(obj)
(может не работать в очень старых браузерах), а Object.getPrototypeOf(a) === Object.getPrototypeOf(b)
дает вам true
. Это тот же объект, также известный как Foo.prototype
.
Ответ 2
Поведение правильное.
[]
транслируется до new Array()
во время выполнения, но создается только один такой массив.
Другими словами, Obj.prototype = {...}
выполняется так же, как и любое другое присваивание.
Ответ 3
Когда вы выполняете var exp1 = new C()
, JavaScript устанавливает exp1.[[Prototype]] = C.prototype
. Когда вы затем обращаетесь к свойствам экземпляра, JavaScript сначала проверяет, существуют ли они на этом объекте напрямую, а если нет, то он выглядит в [[Prototype]]
. Это означает, что все материалы, которые вы определяете в прототипе, эффективно разделяются всеми экземплярами, и вы можете даже позже изменить части прототипа и внести изменения во все существующие экземпляры.