Использование конструктора прототипов в JS

Может кто-нибудь объяснить мне использование Me.prototype.constructor = Me; и почему это необходимо, когда этот код работает и без него?

В объекте прототипа кода создается объект Me, и он создается и заменяется старым прототипом объекта. Почему мне нужно указывать на конструктор Me в заданном выше коде?

function Me(){
    this.name = 'Dejan';

}

function You(){
    this.name = 'Ivan';
}

Me.prototype = new You();

somebody = new Me();

Me.prototype.constructor = Me; // Why?

Me.prototype.foo = function(){
    alert('Proto Me!'); // It always fire up this alert, ether constructor is pointing to Me or not... !
}

You.prototype.foo = function(){
    alert('Proto You!');
}

somebody.foo();
alert(somebody.name); // Alert 'Dejan'

Ответ 1

Это не нужно, и это даже не нужно для instanceof вопреки распространенному мнению (instanceof внутренне проверяет цепочку прототипов и не нуждается в свойстве constructor). Обычно constructor по своей сути является неперечислимым свойством в конструкторе prototype. Таким образом, давая любые объекты, созданные этим конструктором, неперечислимое свойство constructor, указывая на этот конструктор.

Хорошо бы поместить его туда, если вам нужно, в идеале, неперечислимое. В некотором коде предполагается существование .constructor для объектов.

В коде, который вы опубликовали, да, когда вы делаете наследование таким образом, необходимо конструктор reset (если вы хотите его там), потому что объект, который вы создавали в качестве дочернего прототипа, имеет свойство конструктора, указывающее на неправильный конструктор (его конструктор).

В ES5 вы должны:

Child.prototype = Object.create(Parent.prototype, {
  constructor: { value: Child, enumerable: false }
});

edit: Кроме того, стоит упомянуть, что при выполнении наследования с использованием нестандартного __proto__ ему не нужно конструктор reset, потому что __proto__ просто указывает и прототип объекта, который, как говорится, объект на которые будут выполняться, когда собственное свойство не существует. Новый prototype всегда будет иметь свойство, называемое constructor.

Таким образом:

var child = function() {};
child.prototype.__proto__ = parent.prototype;

Вам не нужно устанавливать конструктор, потому что свойство базового конструктора child.prototype все еще существует. Если доступ к нему не требуется, поиск прототипов не требуется.

Ответ 2

Если вы замените строку

Me.prototype.constructor = Me; // Why?

с

console.log(Me.prototype.constructor);
Me.prototype.constructor = Me; // Why?

вы обнаружите, что перед настройкой Me.prototype.constructor есть You, так как Me.prototype является экземпляром You из-за строки

Me.prototype = new You();

Итак, строка с комментарием // Why? необходима, чтобы "исправить" это ошибочное впечатление, которое вы предоставили JavaScript, выполнив наследование таким образом.


По существу проблема возникает, потому что вы пытаетесь использовать прототипное наследование для реализации классического наследования. Прототипное наследование работает на объектных экземплярах и не имеет понятия "классы" или даже, действительно, "типы", но JavaScript делает вещи более запутанными со всеми бизнес-процессами new, .constructor и instanceof.

Более прототипный способ сделать это - отказаться от конструкторов в пользу конструкторов власти, т.е. функций, которые возвращают объект с желаемой формой:

function begetPart(partNumber,  description) {
    return Object.create({}, {
        number: { value: partNumber },
        description: { value: description },
        describe: {
            value: function () {
                alert(this.description);
            }
        }
    });
}

function begetTire(partNumber, speed) {
    return Object.create(
        begetPart(partNumber, "A tire"),
        {
            speed: { value: speed },
            describe: {
                value: function () {
                    alert(this.description + "; speed = " + this.speed);
                }
            }
        }
    );
}

var genericPart = begetPart(1234, "A widget");
genericPart.describe(); // alerts "A widget"

var tire = begetTire(4567, "fast");
tire.describe(); // alerts "A tire; speed = fast"

Здесь мы используем Object.create, чтобы сказать: "Создайте экземпляр объекта на основе этого другого экземпляра объекта". Другой экземпляр - это новый пустой объект для begetPart и новый "экземпляр части" с некоторыми свойствами, предварительно заполненными для begetTire.

Это лучше отражает то, как на самом деле работает JavaScript и прототипное наследование, поскольку в экземплярах объектов прототипа наследования наследуются от других экземпляров объекта, без всякой идеи "типов" или "классов".