Понимание прототипного наследования в JavaScript

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

Первый блок:

function Car(name){
    this.Name = name;
}

Car.prototype.Drive = function(){
    document.write("My name is " + this.Name + " and I'm driving. <br />");
}

SuperCar.prototype = new Car();
SuperCar.prototype.constructor = SuperCar;

function SuperCar(name){
    Car.call(this, name);
}

SuperCar.prototype.Fly = function(){
    document.write("My name is " + this.Name + " and I'm flying! <br />");
}

var myCar = new Car("Car");
myCar.Drive();

var mySuperCar = new SuperCar("SuperCar");
mySuperCar.Drive();
mySuperCar.Fly();

Второй блок:

function Car(name){
    this.Name = name;
    this.Drive = function(){ 
        document.write("My name is " + this.Name + " and I'm driving. <br />");
    }
}

SuperCar.prototype = new Car();

function SuperCar(name){
    Car.call(this, name);
    this.Fly = function(){
        document.write("My name is " + this.Name + " and I'm flying! <br />");
    }
}

var myCar = new Car("Car");
myCar.Drive();

var mySuperCar = new SuperCar("SuperCar");
mySuperCar.Drive();
mySuperCar.Fly();

Почему автор добавил методы Drive и Fly, используя prototype, но не объявляет их как метод this.Drive внутри класса Car и this.Fly в классе SuperCar?

Почему SuperCar.prototype.constructor нужно вернуть на SuperCar? Является ли свойство конструктора переопределенным при установке prototype? Я прокомментировал эту строку, и ничего не изменилось.

Зачем вызывать Car.call(this, name); в конструкторе SuperCar? Не будут ли свойства и методы Car "унаследованы", когда я сделаю

var myCar = new Car("Car");

Ответы

Ответ 1

Два блока отличаются таким образом, что в первом примере Drive() будет существовать только один раз, когда во втором подходе Drive() будет существовать на один экземпляр (каждый раз, когда вы выполняете new Car(), будет создана функция Drive() еще раз). Или другой сказал, что первый использует прототип для хранения функции, а второй - для конструктора. Поиск функций - это конструктор, а затем прототип. Поэтому для поиска Drive() он находит это независимо от того, находится ли он в конструкторе или в прототипе. Использование прототипа более эффективно, потому что обычно вам нужна функция только один раз для каждого типа.

Вызов new в javascript автоматически устанавливает конструктор в прототипе. Если вы переписываете прототип, поэтому вам нужно установить конструктор вручную.

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

Ответ 2

Чтобы добавить к ответ Норберта Хартла, SuperCar.prototype.constructor не нужен, но некоторые люди используют его как удобный способ получить конструктивную функцию объект (объекты SuperCar в этом случае).

Как раз из первого примера, Car.call(это, имя) находится в функции конструктора SuperCar, потому что когда вы это делаете:

var mySuperCar = new SuperCar("SuperCar");

Это то, что делает JavaScript:

  • Создается новый пустой объект.
  • Внутренний прототип нового объекта установлен в "Автомобиль".
  • Выполняется функция конструктора SuperCar.
  • Готовый объект возвращается и устанавливается в mySuperCar.

Обратите внимание, что JavaScript не вызывал Car для вас. Прототипы, являющиеся такими, какие они есть, любое свойство или метод, которые вы не настроили для SuperCar, будут рассмотрены в Car. Иногда это хорошо, например. SuperCar не имеет метода Drive, но он может совместно использовать Car one, поэтому все SuperCars будут использовать тот же метод Drive. В других случаях вы не хотите делиться, как и каждый SuperCar, имеющий собственное имя. Итак, как же все-таки настроить каждое имя SuperCar на его собственную вещь? Вы можете установить this.Name внутри функции конструктора SuperCar:

function SuperCar(name){
    this.Name = name;
}

Это работает, но подождите секунду. Разве мы не сделали то же самое в конструкторе Car? Не хочу повторять. Поскольку Car устанавливает имя уже, позвольте просто называть его.

function SuperCar(name){
    this = Car(name);
}

Укоротите, вы никогда не захотите изменить специальную ссылку на объект this. Помните 4 шага? Повесьте на тот объект, который вам дал JavaScript, потому что это единственный способ сохранить ценную внутреннюю прототипную связь между вашим объектом SuperCar и Car. Итак, как мы устанавливаем Name, не повторяясь и не отбрасывая наш новый объект SuperCar, JavaScript потратил столько особых усилий на подготовку к нам?

Две вещи. Один: значение this является гибким. Два: автомобиль - это функция. Можно позвонить в Car, а не с нетронутым, новым экземпляром объекта, но вместо этого, например, с объектом SuperCar. Это дает нам окончательное решение, которое является частью первого примера в вашем вопросе:

function SuperCar(name){
    Car.call(this, name);
}

Как функция, Car разрешен для вызова с помощью функции метода вызова, которая изменяет значение this внутри Car to экземпляр SuperCar, который мы собираем. Presto! Теперь каждый SuperCar получает свое собственное свойство Name.

Чтобы завернуть, Car.call(this, name) в конструкторе SuperCar дает каждому новому объекту SuperCar свое уникальное свойство Name, но без дублирования кода, который уже находится в Car.

Прототипы не страшны, как только вы их понимаете, но они совсем не похожа на классическую модель ООП класса/наследования. Я написал статью о концепции прототипов в JavaScript. Он написан для игрового движка, который использует JavaScript, но он тот же движок JavaScript, который используется Firefox, поэтому все должно быть релевантным. Надеюсь, это поможет.

Ответ 3

Норберт, вы должны заметить, что ваш первый пример - это в значительной степени то, что Дуглас Крокфорд называет псевдоклассическим наследованием. Что-то об этом нужно отметить:

  • Дважды вы вызовите конструктор Car, один раз из строки SuperCar.prototype = new Car(), а другой - из строки "Конструктор кражи" Car.call(этот... вы можете создать вспомогательный метод для наследования прототипов вместо этого, и ваш конструктор Car должен будет запускать только один раз, делая установку более эффективной.
  • Строка SuperCar.prototype.constructor = SuperCar позволит вам использовать instanceof для идентификации конструктора. Некоторые люди хотят, чтобы другие просто избегали использования экземпляра
  • Ссылки, такие как: var arr = ['one', 'two'], если они определены в супер (например, автомобиль), будут разделяться всеми экземплярами. Это означает, что inst1.arr.push ['three'], inst2.arr.push ['four'] и т.д. Будут отображаться для всех экземпляров! По существу, статическое поведение, которое вы, вероятно, не хотите.
  • Второй блок определяет метод fly в конструкторе. Это означает, что каждый раз, когда он вызывается, создается "объект метода". Лучше использовать прототип для методов! Вы можете, однако, сохранить его в конструкторе, если хотите - вам просто нужно защититься, чтобы вы только на самом деле инициализировали прототип литерала один раз (псевдо): if (SuperCar.prototype.myMethod!= 'Function')... затем определите ваш прототип литерала.
  • 'Зачем вызывать Car.call(это, имя)....': У меня нет времени внимательно посмотреть на ваш код, чтобы я мог ошибаться, но обычно это так, что каждый экземпляр может сохранять собственное состояние для исправления проблемы "статического" поведения в цепочке прототипов, описанной выше.

Наконец, я хотел бы упомянуть, что у меня есть несколько примеров кода TDD JavaScript Inheritance, который работает здесь: TDD JavaScript Inheritance Code and Essay Я хотел бы получить ваши отзывы, поскольку я надеюсь улучшить его и сохранить его открытым исходным кодом. Цель состоит в том, чтобы помочь классическим программистам быстро ускорить работу с JavaScript, а также дополнить исследование как книгами Crockford, так и Zakas.

Ответ 4

Я не уверен на 100%, но я считаю, что разница в том, что второй пример просто дублирует содержимое класса Car в объект SuperCar, а первый связывает прототип SuperCar с классом Car, так что время выполнения изменения в классе Car также влияют на класс SuperCar.

Ответ 5

function abc() {
}

Методы и свойства прототипа, созданные для функции abc

abc.prototype.testProperty = 'Hi, I am prototype property';
abc.prototype.testMethod = function() { 
   alert('Hi i am prototype method')
}

Создание новых экземпляров для функции abc

var objx = new abc();

console.log(objx.testProperty); // will display Hi, I am prototype property
objx.testMethod();// alert Hi i am prototype method

var objy = new abc();

console.log(objy.testProperty); //will display Hi, I am prototype property
objy.testProperty = Hi, I am over-ridden prototype property

console.log(objy.testProperty); //will display Hi, I am over-ridden prototype property

http://astutejs.blogspot.in/2015/10/javascript-prototype-is-easy.html