2017-01-04 3 views
2

Я пытаюсь понять наследование на основе прототипов в JavaScript. Для того, чтобы создать цепочку прототипов с функциями конструктора, вы обычно делаете это таким образом:Тип наследования: Почему вам нужно создать новый объект, вместо того, чтобы напрямую устанавливать прототип?

function Person(name, gender){ 
    this.name = name; 
    this.gender = gender; 
} 

function Male(name){ 
    Person.call(this, name, "male"); 
} 

Male.prototype = Object.create(Person.prototype); 

Object.defineProperty(Male.prototype, "constructor", { 
    enumerable: false, value: Male, writeable: true 
}); 

var person1 = new Male("Chris"); 

Так, Male.prototype создать совершенно новый объект, который имеет внутренний [[Prototype]] набор свойств для родительского объекта. Поскольку это новый объект, вам все равно нужно добавить неперечислимое свойство constructor.

Поскольку существует способ установить [[prototype]] свойство непосредственно, интересно, почему вы не можете сделать это:

function Person(name, gender){ 
    this.name = name; 
    this.gender = gender; 
} 

function Male(name){ 
    Person.call(this, name, "male"); 
} 

Object.setPrototypeOf(Male.prototype, Person.prototype); 

var person1 = new Male("Chris"); 

С помощью этого решения вы просто установите [[prototype]] свойство Male.prototype для родительского объекта. (До ES6 вы могли использовать свойство __proto__ для установки прототипа.) Вам не нужно создавать новый объект (поскольку, насколько я знаю, js runtime автоматически прикрепляет новый объект prototype к каждой функции), и вы не нужно явно создавать свойство constructor, потому что оно уже существует.

Мой вопрос: почему никто не использует этот подход для установки цепи прототипов? В чем проблема?

+0

FYI, ваша версия 'Object.create' может быть сделана немного более чисто, используя второй аргумент для определения свойства' constructor'. –

+0

Линия 'Object.defineProperty..' на самом деле не является общим способом создания конструктора. Вся эта инструкция (эти 3 строки) просто будет: 'Male.prototype.constructor = Person;' –

+0

@ScottMarcus: Это делает ее перечислимой. Использование 'defineProperty' позволяет вам установить правильный дескриптор свойства. –

ответ

-1

Это тонкий, но ОЧЕНЬ важный момент. Если вы установите прототип Male на прототип Person, они будут делить ONE экземпляр объекта. Любые изменения прототипа Person будут влиять на ВСЕ экземпляры объектов Person и Maleи наоборот. Теперь изначально это похоже на желаемое поведение, но вы должны оставить здесь понятия «типы» отдельно от «экземпляров».

Давая Maleнового экземпляр Person как его прототип, вы получаете преимущество наследования, но де-пар фактического экземпляр Person, что все Person экземпляров используют от экземпляра что все Male экземпляров будет использовать.

Это позволяет вносить изменения в Male.prototype без ущерба для любых случаев Person.

+0

* «Если вы установили прототип' Male' в прототип 'Person' ... * * Это было бы верно, за исключением того, что это не то, что делает OP. OP переключает следующий элемент в цепочке прототипов «Male.prototype» из «Object.prototype» на «Person.prototype», поэтому на поверхности он кажется действительным, но я не знаю, имеет ли он такую ​​же производительность проблемы, связанные с изменением цепи прототипов. –

+0

@squint MDN утверждает, что производительность может очень сильно пострадать. –

+0

Да, я знаю об этом в общем случае, но это одноразовое изменение цепи прототипов самих конструкторов, а не отдельных экземпляров. Я не хотел бы претендовать ни на что, без тестирования. –

0

Это интересный вопрос. Я не знал о существовании метода setPrototypeOf. По-видимому, производительность хуже, чем использование Object.create

Если вам небезразлична производительность, вам следует избегать установки [[Prototype]] объекта. Вместо этого создайте новый объект с желаемым [[Prototype]], используя Object.create().

Смотреть еще в ссылке от developer.mozilla.org

0

Я имел то же сомнение, несколько дней назад, и я спросил об этом здесь: https://softwareengineering.stackexchange.com/questions/343778/when-doing-inheritance-why-not-simply-modify-the-prototype-property-of-the

По-видимому, нет единого мнения относительно того, почему мы не должны» t do Object.setPrototypeOf(Child.prototype, Parent.prototype), но есть общие предупреждения, брошенные у нас из Mozilla (MDN).

Предупреждение: изменение [[Prototype]] объекта по характеру, как современные движки JavaScript оптимизируют доступ к ресурсам, очень медленную операцию, в каждом браузере и в JavaScript-движке. Эффекты на производительность изменяющегося наследования тонкие и обширные и не ограничиваются просто временем, потраченным в obj .__ proto__ = ... statement, но могут распространяться на любой код, который имеет доступ к любому объекту, чей [[Prototype] ] изменен. Если вы заботитесь о производительности, вам следует избегать установки [[Prototype]] объекта. Вместо этого создайте новый объект с желаемым [[Prototype]], используя Object.create().

Итог: не делайте этого!

Он порождает неопределенность в коде.

Смежные вопросы