Почему использование конструкторов не рекомендуется при создании прототипов?

10

Краткая справка: в JavaScript функция конструктора для каждого типа объекта имеет prototypeсвойство. prototypeОтносится к объекту , который каждый построенный объект использует в качестве следующего шага вверх в своей цепи прототипов. Если вы хотите, чтобы один тип был присущ другому типу, вы можете установить prototypeдля дочернего типа новый экземпляр родительского типа.

Например:

var Parent = function() { /* constructor business */ }
Parent.prototype.parentProp = "some parent property";

var Child = function() { /* constructor business */ }
Child.prototype = /*** !! Some prototype object goes here !! ***/

Мой вопрос спрашивает о том, какой код должен идти в " Some prototype object goes here" месте в приведенном выше коде. Мой первый инстинкт - создать экземпляр родительского объекта (т. Е. new Parent()), Но в комментарии к ответу « Является ли это безопасным способом копирования одного прототипа объекта в другой? , один пользователь пишет:

Нет, не используйте new bar()для объекта-прототипа!

(... это мнение я видел во многих SO-ответах и ​​комментариях, но это единственный пример, который у меня есть на данный момент.)

Другой вариант - использовать Object.create(Parent.prototype)как Child.prototype. Насколько я знаю, это также создает новый Parentэкземпляр, но он не запускает Parentконструктор.

Может кто-нибудь объяснить, почему следует избегать запуска функции конструктора при создании объекта-прототипа из родительского типа? Возникает ли какая-то серьезная техническая проблема (возможно, с несколькими уровнями наследования)? Или такой шаблон является неправильным использованием конструкторов, что противоречит некоторой передовой практике создания прототипа (например, запуск конструктора при создании прототипа нарушает некоторое разделение задач)?

apsillers
источник

Ответы:

5

Потому что это функция, которую не нужно вызывать. newничем не отличается от обычного вызова функции в Javascript.

Конструктор может делать больше, чем просто устанавливать поля. Например, если он проверяет входящие данные, то вы вызовете ошибку проверки, когда вы просто пытаетесь установить цепочку наследования.

И вам не нужно Object.create, этого достаточно:

function objectCreate( proto ) {
    function T(){}
    T.prototype = proto;
    return new T();
}
Esailija
источник
Но это именно так, как Object.createэто реализовано.
Кейси Чу,
@CaseyChu совсем нет. И я упомянул об этом, потому что в связанном посте кто-то сказал « Object.createне работает в IE8» - это просто бесполезный комментарий, когда вы можете реализовать его для этого варианта использования за 2 секунды в любом браузере.
Esailija
2

Теперь, когда мое понимание немного расширилось, я хотел бы основываться на ответе Эсайлии на конкретном примере:

Особое беспокойство вызывает то, что конструктор может устанавливать специфичные для экземпляра свойства. Таким образом, если вы используете объект-прототип, созданный с помощью new, все ваши дочерние экземпляры могут совместно использовать одно определяемое конструктором свойство экземпляра из прототипа.

Например, предположим, что Parentкаждый экземпляр имеет уникальное idсвойство, установленное как время создания:

var Parent = function() { this.id = Math.random(); }
Parent.prototype.parentProp = "some parent property";

var Child = function() { /* constructor business */ }
Child.prototype = new Parent();

Это заставит все Child экземпляры совместно использовать одно idсвойство прототипа, унаследованное от одного и того же new Parent()объекта, используемого как Child.prototype.

Лучшим подходом для этого случая является использование Object.createи непосредственный вызов родительского конструктора (при необходимости) внутри дочернего конструктора:

var Parent = function() { this.id = Math.random(); }
Parent.prototype.parentProp = "some parent property";

var Child = function() {
    // run `this` through the Parent constructor function
    Parent.apply(this, arguments);
}
Child.prototype = Object.create(Parent.prototype);
apsillers
источник