JavaScript ООП: объекты меняют свой прототип (для всех других объектов, использующих тот же прототип)

function NamedRoundedBlock(){
    var name = this.makeFeild("name");
    name.className = "Block NamedRound name";
    this.element.className = "Block NamedRound root";
    this.element.appendChild(name);
}
NamedRoundedBlock.prototype = new Block();
NamedRoundedBlock.prototype.constructor = NamedRoundedBlock;

Вот код, кто-нибудь знает, что я сделал не так?

Моя проблема в том, что если я создам два объекта NamedRoundedBlock и изменю свойство this.element одного (путем изменения атрибута или чего-то еще), другие тоже изменятся.

Кроме того, небольшая дополнительная деталь, this.makeFeild и this.element оба установлены в блоке.


person user1877408    schedule 04.01.2014    source источник
comment
Я понятия не имею, что вы сделали не так, потому что вы не сказали, в чем проблема.   -  person Chris Hayes    schedule 04.01.2014
comment
Меня тоже смущает название. Объекты не меняют свои прототипы волшебным образом. Вы можете уточнить?   -  person Ray Toal    schedule 04.01.2014
comment
Извините, я немного отредактирую вопрос   -  person user1877408    schedule 04.01.2014
comment
Спасибо за разъяснения. JavaScript ведет себя так, как рекламируется здесь. Прототип — это просто место для общих свойств. Изменения, которые вы вносите в прототип, видны всем объектам.   -  person Ray Toal    schedule 04.01.2014
comment
Это немного раздражает. Каков наилучший способ обойти это?   -  person user1877408    schedule 04.01.2014
comment
Напишу ответ, раз сек.   -  person Ray Toal    schedule 04.01.2014
comment
Ответил, но вопрос только что был помечен как обман.   -  person Ray Toal    schedule 04.01.2014
comment
Вы только что нашли одну из причин, по которой вам не следует создавать экземпляр Parent для установки части прототипа наследования Child. Вы по-прежнему можете выполнять Parent.apply(this,arguments); и теневые экземпляры родительского экземпляра, но вам все равно не следует Child.prototype=new Parent(); использовать Object.create с полифилом, если это необходимо: stackoverflow. com/a/16063711/1641941   -  person HMR    schedule 04.01.2014


Ответы (2)


Ознакомьтесь с Традиционное наследование ООП в JavaScript

Сообщение в блоге выше может помочь ответить на некоторые из ваших вопросов.

В частности, в нем подробно рассматривается определение нового конструктора для объекта, который «наследуется» от родителя. Это один из ваших вопросов? Возьмите это, например:

NamedRoundedBlock.prototype = Object.create(
    Block.prototype,
        {
            "constructor": { 
                configurable: true,
                enumerable: false,
                writable: true,
                value: 'NamedRoundedBlock'
            }
        }
);

Вышеупомянутый создал объект на основе прототипа Block, но он определяет свой собственный конструктор, а также устанавливает его как не перечисляемый.

Кроме того, должна ли первая строка быть примерно такой?:

function NamedRoundedBlock() {
    this.name = this.makeFeild("name");
    //etc.

Кроме того, независимо от проблемы, я был бы осторожен с написанием «Фейлд» (пишется как «Поле»), поскольку проблемы с правописанием иногда могут привести к трудно обнаруживаемым ошибкам позже в коде.

person Josh Beam    schedule 04.01.2014
comment
Ссылки - плохой ответ! Постарайтесь уточнить в своем посте, так как ссылки могут меняться и меняются. - person James Bruckner; 04.01.2014
comment
Второй параметр в Object.create не работает в старых браузерах, и хороший полифил должен выдавать ошибку: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ (см. throw new Error) - person HMR; 04.01.2014

Прототип — это место для общих свойств.

Рассмотреть возможность:

var protoCircle - {x: 0, y: 0, radius: 1}

Это означает, что каждый круг будет иметь эти свойства как значения по умолчанию. Итак, если я сделаю круг с этим прототипом:

var c1 = Object.create(protoCircle);
c1.x = 3;

var c2 = Object.create(protoCircle);
c2.y = 5;

тогда круг c1 находится в точке (3,0) с радиусом 1, а c2 находится в точке (0,5) с радиусом 1. Это потому, что c1 имеет только одно собственное свойство (x) и два унаследованных свойства (y и radius), которые он берет из своего прототипа. Если я изменю protoCircle.radius, то это изменение будет видно по двум кругам. Так устроен JavaScript! Этот дизайн позволяет целой группе объектов совместно использовать значение по умолчанию, поэтому вам не нужно хранить его в каждом объекте. Вы просто помещаете свойства, уникальные для каждого объекта внутри каждого объекта, и позволяете использовать свойства по умолчанию в прототипе. В приведенном выше случае, если 99% кругов имеют радиус 1, нам не нужно хранить радиусы в наших отдельных кругах.

В вашем случае вы помещаете свойство с именем element в прототип. Все объекты, использующие этот прототип, будут иметь одинаковое значение element. Итак, если вы скажете

x.element.className = 'something'

тогда это фактически делает y.element.className также something, предполагая, что y использует тот же прототип, что и x.

Если вы хотите, чтобы каждый из именованных округленных блоков имел разные элементы, вам нужно сделать следующее:

function NamedRoundedBlock(){
    ...
    this.element = {}
    this.element.className = "Block NamedRound root";
    this.element.appendChild(name);
}
person Ray Toal    schedule 04.01.2014