Члены объекта Javascript, которые прототипируются в виде массивов, становятся общими для всех экземпляров класса - PullRequest
22 голосов
/ 13 декабря 2010

Кто-нибудь замечал такое поведение раньше? Это действительно отбросило меня ... Я ожидал, что прототипированные массивы будут частными для каждого экземпляра класса, а не общими для всех экземпляров класса.

Может ли кто-нибудь подтвердить, что это правильное поведение, и, возможно, объяснить это поведение более подробно?

Обратите внимание на закомментированный код и его влияние на поведение скрипта.

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

Большое спасибо.

Ответы [ 3 ]

33 голосов
/ 13 декабря 2010

Прототип объекта - это просто объект.Свойства прототипа являются общими для всех объектов, которые наследуют от этого объекта.Копии свойств не создаются, если вы создаете новый экземпляр «класса» (классы в 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().

Правилоthumb: Экземпляр -специфичные данные должны быть назначены экземпляру внутри конструктора , shared данных (как методы) должны быть назначенык прототипу .


YouВозможно, вам захочется прочитать Подробную информацию об объектной модели , которая описывает различия между языками на основе классов и прототипами и как на самом деле работают объекты.

Обновление:

Вы можете получить доступ к прототипу объекта через Object.getPrototypeOf(obj) (может не работать в очень старых браузерах), а Object.getPrototypeOf(a) === Object.getPrototypeOf(b) дает вам true.Это тот же объект, также известный как Foo.prototype.

1 голос
/ 13 декабря 2010

Поведение правильное. [] транслируется в new Array() во время выполнения, но когда-либо создается только один такой массив.

Другими словами, Obj.prototype = {...} выполняется так же, как и любое другое назначение.

0 голосов
/ 14 ноября 2017

Когда вы делаете var exp1 = new C(), JavaScript устанавливает exp1.[[Prototype]] = C.prototype. Когда вы затем обращаетесь к свойствам экземпляра, JavaScript сначала проверяет, существуют ли они непосредственно на этом объекте, и если нет, он выглядит в [[Prototype]]. Это означает, что все, что вы определяете в прототипе, эффективно используется всеми экземплярами, и вы можете даже позже изменить части прототипа, и эти изменения появятся во всех существующих экземплярах.

...