2015-05-23 4 views
4

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

Особенности:

Мой текущий подход выглядит следующим образом.

var Person = function(name) { 
    this.name = name; 

    this.greeting = function() { 
     alert("Greetings, I am " + name); 
    } 

} 

var bob = new Person("bob"); 
bob.greeting(); 

var Woman = function(name) { 
    Person.call(this, name); 
    this.gender = "female"; 
} 

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

var brenda = new Woman("brenda"); 
brenda.greeting(); 

Person.prototype.eats = true; 

alert(brenda.eats); 

Проверив код я нашел это отлично работает - насколько я могу сказать, - но мне сказали, что это не самый лучший подход, и что я должен определить конструктор, как это:

Woman.prototype.constructor = Woman; 

И не используйте метод Person.call в моем методе фактического конструктора. Две вещи, когда мне сказали, что я не вижу простого способа передать параметры с помощью второго подхода, а также, почему? То, что я делаю, кажется, работает нормально.

Я что-то упустил?

Есть ли случаи, когда то, что я делаю, даст непредсказуемые ошибки?

Может ли кто-нибудь дать окончательный «правильный» подход и причину этого?

+0

'prototype.call'? Вы имели в виду 'Person.call'? – thefourtheye

+0

Я думаю ... и я мог ошибаться здесь, что метод вызова принадлежит Object.prototype и наследуется. Так Person.call является сокращением для Person.prototype.call() .. так вот что я считаю. –

+0

FYI, 'Person.call' - это не то же самое, что' Person.prototype.call'. 'Person.protoype' не является функцией, его нельзя назвать. –

ответ

5

Что вы, я думаю, считается лучшим подходом. Дело вы поднимаете однако не имеет никакого значения:

Woman.prototype.constructor = Woman; 

не является заменой для Person.call(...). На самом деле, эти две вещи не имеют ничего общего, они служат разным целям:

  • Вызывать родительский конструктор в детском конструкторе гарантирует, что экземпляр ребенок правильно инициализирован. Это как позвонить super() в ES6 или на других языках.

  • Присвоение constructor просто восстанавливает исходное значение Women.prototype.constructor. Если вы этого не сделали, brenda.constructor будет ссылаться на Person. Это не влияет на внутреннюю работу вашего наследования, но другой код, который потребляет ваш объект, может рассчитывать на constructor, имеющий правильное значение. См. Также Advantages of setting the "constructor" Property in the "prototype".

Таким образом, ответ на ваш вопрос: вы должны сделать то и другое.

Иногда вы видите назначение, выполняемое как часть вызова Object.create. Это еще более точным, так как он воссоздает исходные характеристики объекта (например, не-перечислимости):

Woman.prototype = Object.create(Person.prototype, { 
    constructor: {value: Woman, writable: true} 
}); 

FWIW, я считаю, одна из причин введения class в ES6 было уменьшить путаницу вокруг конструкторских функций и прототипов.Это не означает, что вы не должны знать об этом, в конце концов, class более или менее просто синтаксический сахар, но это только делает наследование немного проще:

class Woman extends Person { // almost like Woman.prototype = Object.create(Person.prototype) 
    constructor(name) { 
    super(name); // almost like Person.call, but enforced by the interpreter 
    this.gender = "female"; 
    } 
} 
+0

Феликс, я не сомневаюсь, что вы правы, но для полноты вы могли бы объяснить, почему мне нужно явно определить конструктор, пожалуйста? Мне кажется странным, потому что ясно, когда я использую новый оператор, как в woman = new Woman(), он говорит, что Женщина - это конструктор. Разве это необходимо для объектов, которые наследуются от класса «Женщина»? –

+0

Спасибо Феликс. Еще один вопрос. В соответствии с приведенным ниже ответом многие источники предполагают, что вместо Woman.prototype = Object.create (Person.protype), которые я понимаю как составляющие глубокую копию соответствующего прототипа, вместо этого я должен просто написать Woman.prototype = new Person() ... это действительно то же самое? –

+2

Это может быть одно и то же, но это концептуально неправильно. См. Мой ответ здесь: [Преимущества использования 'Object.create' для наследования] (http://stackoverflow.com/q/17392857). –

0

это ООП подход в JS

var Person = function(name) { 
    this.name = name; 
} 

Person.prototype.greeting = function(){ 
    var newPerson = new Person(this.name); 
    alert("Greetings, I am " + name); 
    return newPerson; 
} 

Person.prototype.toString=function(){ 
    return '[Person "'+this.name+'"]'; 
} 

Woman.prototype = new Person(); 

Woman.prototype.constructor=Woman;  

function Woman(name){ 
    this.name=name; 
} 

Woman.prototype.toString=function(){ 
    return '[Woman "'+this.name+'"]'; 
} 

var somePerson = new Person('Mr.'); 
var myPerson = new Woman('She'); 
alert('somePerson is '+somePerson); // results in 'somePerson is [Person "Mr."]' 
alert('myPerson is '+myPerson);    // results in 'myPerson is [Woman "She"]' 

myPerson.greeting();     // calls a method inherited from Person 
+1

Это нехороший подход. У этого есть немало недостатков по сравнению с 'Object.create'. –

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