Понимание прототипного наследования в JavaScript
я новичок в JavaScript ООП. Не могли бы вы объяснить разницу между следующими блоками кода? Я проверил и оба блока работают. Какова лучшая практика и почему?
первый блок:
function Car(name){
this.Name = name;
}
Car.prototype.Drive = function(){
console.log("My name is " + this.Name + " and I'm driving.");
}
SuperCar.prototype = new Car();
SuperCar.prototype.constructor = SuperCar;
function SuperCar(name){
Car.call(this, name);
}
SuperCar.prototype.Fly = function(){
console.log("My name is " + this.Name + " and I'm flying!");
}
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(){
console.log("My name is " + this.Name + " and I'm driving.");
}
}
SuperCar.prototype = new Car();
function SuperCar(name){
Car.call(this, name);
this.Fly = function(){
console.log("My name is " + this.Name + " and I'm flying!");
}
}
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
? Это constructor
свойство переопределяется, когда prototype
установлен? Я закомментировал эту строку и ничего не изменилось.
зачем называть Car.call(this, name);
на SuperCar
конструктор? Не будут свойства и методы Car
быть "наследуется", когда я делаю
var myCar = new Car("Car");
6 ответов:
два блока отличаются таким образом, что в первом примере
Drive()
будет существовать только один раз, а при втором подходеDrive()
будет существовать на экземпляр (каждый раз, когда вы делаетеnew Car()
функцииdrive()
будет создана вновь). Или другой сказал, что первый использует прототип для хранения функции, а второй-конструктор. Поиск функций-это конструктор, а затем прототип. Так что для просмотраDrive()
Он находит его независимо от того, находится ли он в конструкторе или в прототипе. Использование прототипа более эффективно, потому что обычно вам нужна функция только один раз для каждого типа.The
new
вызов в javascript автоматически устанавливает конструктор в прототипе. Если вы перезаписываете прототип, поэтому вам нужно установить конструктор вручную.наследование в javascript не имеет ничего подобного
super
. Поэтому, если у вас есть подкласс, единственный шанс вызвать супер конструктор-это его имя.
добавить ответ Норберта Хартла, суперкар.прототип.конструктор не нужен, Но некоторые люди используют его как удобный способ получения функции построения объекта (в данном случае объектов суперкара).
только из первого примера, автомобиль.вызов (это, имя) находится в функции конструктора суперкара, потому что при этом:
var mySuperCar = new SuperCar("SuperCar");
вот что делает JavaScript:
- свежий, пустой объект создать экземпляр.
- внутренний прототип нового объекта установлен на автомобиль.
- запускается функция конструктора суперкара.
- готовый объект возвращается и устанавливается в mySuperCar.
обратите внимание, как JavaScript не вызывал автомобиль для вас. Прототипы, как они есть, любое свойство или метод, который вы не устанавливаете для суперкара, будут искать в автомобиле. Иногда это хорошо, например, суперкар не имеет метода привода, но он может делиться Автомобиль один, поэтому все суперкары будут использовать один и тот же метод привода. В других случаях вы не хотите делиться, как каждый суперкар, имеющий свое собственное имя. Итак, как можно установить имя каждого суперкара на его собственную вещь? Вы могли бы установить this.Name внутри функции конструктора суперкара:
function SuperCar(name){ this.Name = name; }
это работает, но подожди секунду. Разве мы не делали то же самое в конструкторе автомобилей? Не хочу повторяться. Поскольку автомобиль уже задает имя, давайте просто позвоним оно.
function SuperCar(name){ this = Car(name); }
Упс, вы никогда не хотите изменить особенный
this
ссылка на объект. Помню 4 шага? Держитесь за этот объект, который дал вам JavaScript, потому что это единственный способ сохранить драгоценную внутреннюю связь прототипа между вашим объектом суперкара и автомобилем. Итак, как же нам установить имя, не повторяясь и не выбрасывая наш свежий суперкар объект JavaScript потратил столько особых усилий, чтобы подготовиться к нам?две вещи. Во-первых: значение
this
гибкий. Два: Автомобиль-это функция. Можно назвать автомобиль не с первозданным, свежим экземпляром объекта, а вместо этого, скажем, с объектом суперкара. Это дает нам окончательное решение, которое является частью первого примера в вашем вопросе:function SuperCar(name){ Car.call(this, name); }
как функция, Car разрешается вызывать с помощью функции вызов метода, что меняет значение
this
внутри автомобиля к экземпляру суперкара, который мы создаем. Вуаля! Теперь каждый суперкар получает это свойство собственного имени.чтобы закончить,
Car.call(this, name)
в конструкторе суперкара каждый новый объект суперкара имеет собственное уникальное свойство имени, но без дублирования кода, который уже находится в автомобиле.прототипы не страшны, как только вы их понимаете, но они совсем не похожи на классическую модель класса/наследования ООП. Я написал статью о концепция прототипов в JavaScript. Он написан для игрового движка, который использует JavaScript, но это тот же движок JavaScript, используемый Firefox, поэтому все это должно быть актуально. Надеюсь, это поможет.
Норберт, вы должны отметить, что ваш первый пример-это в значительной степени то, что Дуглас Крокфорд называет псевдоклассическим наследованием. Что-то вещи, чтобы отметить об этом:
- вы вызовете конструктор автомобиля дважды, один раз из суперкара.прототип = новый автомобиль () линия и другой из" конструктора кражи " линии автомобиля.звоните(это...вместо этого вы можете создать вспомогательный метод для наследования прототипов, и ваш конструктор автомобиля должен будет запускаться только один раз, делая установку больше эффективный.
- Суперкара.прототип.конструктор = суперкар линия позволит вам использовать instanceof для идентификации конструктора. Некоторые люди хотят этого, другие просто избегают использования instanceof
- Reference vars like: var arr = ['one','two'] при определении на супер (например, автомобиль) будет совместно использоваться всеми экземплярами. Это означает, inst1 в.прибытие.push ['three'], inst2.прибытие.нажимаем['четыре'] и т. д., будет отображаться для всех случаев! По сути, статическое поведение, которое вы, вероятно, не делаете хотеть.
- вы второй блок определяет метод fly в конструкторе. Это означает, что каждый раз, когда он вызывается, будет создан "объект метода". Лучше использовать прототип для методов! Однако вы можете сохранить его в конструкторе, если хотите - вам просто нужно защитить, чтобы вы только фактически инициализировали литерал прототипа один раз (псевдо): if (суперкар.прототип.метод mymethod != 'функция')...затем определите свой прототип литерала.
- зачем звонить в машину.вызов(это имя)....'Я не у вас есть время внимательно посмотреть на ваш код, поэтому я могу ошибаться, но обычно это делается для того, чтобы каждый экземпляр мог сохранить свое собственное состояние, чтобы исправить проблему поведения "статичности" цепочки прототипов, которую я описал выше.
наконец, я хотел бы упомянуть, что у меня есть несколько примеров кода наследования TDD JavaScript, который работает здесь: TDD JavaScript код наследования и эссе я хотел бы получить ваши отзывы, как я надеюсь, чтобы улучшить его и сохранить его с открытым исходным кодом. Цель чтобы помочь классическим программистам быстро освоить JavaScript, а также дополнить исследование как Крокфорд, так и Закас книги.
Я не уверен на 100%, но я считаю, что разница заключается в том, что второй пример просто дублирует содержимое класса автомобиля в объект суперкара, в то время как первый связывает прототип суперкара с классом автомобиля, так что изменения времени выполнения класса автомобиля влияют и на класс суперкара.
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
здесь есть несколько вопросов:
не могли бы вы объяснить разницу между следующими блоками кода. Я проверил и оба блока работают.
первый только создает один
Drive
функция, вторая создает два из них: один наmyCar
иmySuperCar
.вот код, который даст разные результаты при выполнении первого или второго блока:
myCar.Fly === mySuperCar.Fly // true only in the first case Object.keys(myCar).includes("Fly") // true only in the second case Object.keys(Car.prototype).length === 0 // true only in the second case
какова лучшая практика и почему?
Почему автор добавилDrive
иFly
методы с использованиемprototype
, но не объявляет их какthis.Drive
внутриCar
класс аthis.Fly
наSuperCar
класса?лучше определить методы на прототипе, потому что:
- каждый способ определяется только один раз
- каждый метод также доступны для экземпляров, которые были созданы без выполнения конструктора (что имеет место при вызове
Object.create(Car.prototype)
);- вы можете проверить, на каком уровне в цепочке прототипов экземпляра был определен определенный метод.
почему
SuperCar.prototype.constructor
нужно установить обратно вSuperCar
? Этоconstructor
свойство переопределяется, когдаprototype
установлен? Я закомментировал эту строку и ничего не изменилось.The
constructor
свойство не переопределено, когдаprototype
установлен. Но конструкторnew Car()
иCar
, так что если вы установилиnew Car()
доSuperCar.prototype
, то, очевидно,SuperCar.prototype.constructor
иCar
.до тех пор, пока вы не переназначите
prototype
, есть инвариантность:Constructor.prototype.constructor === Constructor
. Например, это верно дляCar
:Car.prototype.constructor === Car
, но это в равной степени верно дляArray
,Object
,String
, ...так далее.но если вы переназначите другой объект на
prototype
, эта инвариантность нарушена. Обычно это не проблема (как вы заметили), но лучше восстановить его, потому что он отвечает вопрос " какой конструктор использует этот объект прототипа при создании новых экземпляров?" некоторый код может сделать такой осмотр и зависеть от него. Смотрите " зачем нужно устанавливать конструктор прототипа?" для таких случаев.зачем называть
Car.call(this, name);
наSuperCar
конструктор? Не будут ли свойства и методы автомобиля "унаследованы", когда я делаюvar myCar = new Car("Car");
если вы этого не сделаете
Car.call(this, name);
затем вашSuperCar
экземпляр не будет свойство name. Вы могли бы, конечно, решить просто сделатьthis.name = name;
вместо этого, который просто копирует код, который находится вCar
конструктор, но в более сложных ситуациях было бы плохой практикой иметь такое дублирование кода.не было бы полезно, чтобы позвонить
new Car(name)
внутриSuperCar
конструктор, Как создать другое объект, в то время как вам действительно нужно расширить