В предыдущей статье была введена концепция прототипа, и взаимосвязь между тремя хорошими друзьями конструктора, объекта прототипа и экземпляром в JavaScript: у каждого конструктора есть «опекун» - объект прототипа, а также «положение» конструктора в сердце объекта прототипа. Они влюблены, но этот экземпляр «тайно влюблен» с объектом прототипа, и она также сохраняет положение объекта прототипа в своем сердце.
Сам JavaScript является не объектно-ориентированным языком, а языком на основе объекта. Для тех, кто привык к другим языкам OO, сначала это немного неудобно, потому что здесь нет концепции «класса», или нет различий между «классом» и «экземпляром», не говоря уже о разнице между «родительским классом» и «подклассом». Итак, как эти груды объектов в JavaScript связаны таким образом?
К счастью, JavaScript предоставил метод реализации «наследования» в начале своего дизайна. Прежде чем понять «наследование», мы теперь поймем концепцию прототипов цепочек.
Прототип цепочка
Мы знаем, что прототипы имеют указатель на конструктор. Что если мы сделаем объект прототипа подкласса равенным другому экземпляру нового SuperClass ()? В это время объект прототипа подкласса содержит указатель на прототип Superclass, а прототип Superclass также содержит указатель на суперклассный конструктор. Полем Полем Таким образом, создается прототипная цепь.
Конкретный код заключается в следующем:
function superclass () {this.name = "women"} superclass.prototype.saywhat = function () {return this.name + ": i`ma Girl!"; } function subclass () {this.subname = "Ваша сестра"; } Subclass.prototype = new superclass (); Subclass.prototype.subsayWhat = function () {return this.subname + ": i'ma Beautiful Girl"; } var sub = new subClass (); console.log (sub.saywhat ()); // Женщины: I'ma Girl!Используйте прототип цепочки для достижения наследования
Из приведенного выше кода мы видим, что подкласс наследует свойства и методы суперкласса. Эта унаследованная реализация заключается в присвоении экземпляра SuperClass объекту прототипа подкласса. Таким образом, прототип объекта подкласса перезаписывается экземпляром суперкласса, обладающим всеми его свойствами и методами, а также с указанием на объект прототипа SuperClass.
Есть некоторые вещи, на которые необходимо обратить внимание при реализации наследования с использованием цепочек прототипа:
Обратите внимание на изменения в конструкторе после наследства. Здесь конструктор подразделения указывает на Superclass, потому что прототип подкласса указывает на Superclass. При понимании цепочки прототипа не игнорируйте объект объекта по умолчанию в конце, поэтому мы можем использовать встроенные методы, такие как ToString во всех объектах.
При реализации наследования через цепочку прототипа вы не можете использовать буквальное определение метода прототипа, потому что это переписывает объект прототипа (также представленную в предыдущей статье):
function superclass () {this.name = "women"} superclass.prototype.saywhat = function () {return this.name + ": i`ma Girl!"; } function subclass () {this.subname = "Ваша сестра"; } Subclass.prototype = new superclass (); Subclass.prototype = {// Объект прототипа перезаписан здесь, потому что атрибуты и методы суперкласса не могут быть унаследованы subsaywhat: function () {return this.subname + »: i`ma Beautiful Girl"; }} var sub = new subClass (); console.log (sub.saywhat ()); // typeerror: неопределенная не является функциейПроблема с обменом экземплярами. При объяснении прототипа и конструктора ранее мы однажды представили, что прототипы, содержащие атрибуты типа эталонного типа, будут переданы всеми экземплярами. Аналогичным образом, свойства эталонного типа в прототипе «родительского класса» также будут использоваться в прототипе. Когда мы изменяем атрибуты типа ссылочного типа «родительского класса» через прототип наследование, будут затронуты все остальные случаи, унаследованные от прототипа. Это не только тратит ресурсы, но и явление, которое мы не хотим видеть:
function superclass () {this.name = "Женщины"; this.bra = ["a", "b"]; } function subclass () {this.subname = "Ваша сестра"; } Subclass.prototype = new superclass (); var sub1 = new subclass (); sub1.name = "man"; sub1.bra.push ("c"); console.log (sub1.name); // man console.log (sub1.bra); // ["a", "b", "c"] var sub2 = new subclass (); console.log (sub1.name); // woman console.log (sub2.bra); // ["a", "b", "c"]Примечание. Добавьте элемент в массив здесь, все экземпляры, унаследованные от SuperClass, будут затронуты, но если вы измените атрибут имени, это не повлияет на другие экземпляры, поскольку массив является ссылочным типом, а имя является примитивным типом.
Как решить проблему обмена экземплярами? Давайте продолжим смотреть вниз ...
Классическое наследование (кража конструктора)
Поскольку мы представили, что мы редко используем прототипы для определения одних объектов, в реальном развитии мы редко используем прототипные сети. Чтобы решить проблему обмена типами ссылок, разработчики JavaScript представили классический шаблон наследования (некоторые люди называют заимствованным конструктором наследием). Его реализация очень проста в названии супертипов конструкторов в подтипе конструкторов. Нам нужно использовать функцию Call () или Apply (), предоставленная JavaScript, давайте посмотрим на пример:
function superclass () {this.name = "Женщины"; this.bra = ["a", "b"];} function subclass () {this.subname = "Ваша сестра"; // Присвоить область суперкласса текущему конструктору для реализации наследования superclass.call (this);} var sub1 = new subclass (); sub1.bra.push ("c"); console.log (sub1.bra); // ["a", "," c "] var sub2 = new Subclass (); console.log (sub2.bra); // ["a", "b"]Superclass.call (это); Это предложение означает, что работа по инициализации суперклассского конструктора называется в среде подкласса (контекст) (контекст), так что каждый экземпляр будет иметь свою собственную копию атрибута бюстгальтера, которая не будет влиять друг на друга.
Однако этот метод реализации все еще не идеален. Поскольку введен конструктор, мы также сталкиваемся с проблемой с конструктором, упомянутым в предыдущей статье: если в конструкторе есть определение метода, то существует отдельная ссылка на функцию ни одного из экземпляров. Наша цель - поделиться этим методом, и методы, которые мы определяем в прототипе Supertype, не могут быть вызваны в экземпляре подтипа:
function superclass () {this.name = "Женщины"; this.bra = ["a", "b"]; } Superclass.prototype.saywhat = function () {console.log ("hello"); } function subclass () {this.subname = "Ваша сестра"; Superclass.call (это); } var sub1 = new subClass (); console.log (sub1.saywhat ()); // typeerror: неопределенная не является функциейЕсли вы прочитали предыдущую статью об объектах и конструкторах прототипа, вы уже должны знать ответ на решение этой проблемы, то есть следуйте подпрограмме предыдущей статьи и использовать «комбинированный удар»!
Комбинация наследования
Комбинация наследования - это способ объединить преимущества цепочки прототипа и конструктора, а также объединить их для достижения наследования. Проще говоря, он должен использовать цепочку прототипа для наследственных атрибутов и методов, и использовать заимствованные конструкторы для реализации наследования атрибутов экземпляра. Это не только решает проблему обмена атрибутами экземпляров, но также позволяет унаследовать атрибуты и методы супертипа:
function superclass () {this.name = "Женщины"; this.bra = ["a", "b"]; } Superclass.prototype.saywhat = function () {console.log ("hello"); } function subclass () {this.subname = "Ваша сестра"; Superclass.call (это); // Второй вызов SuperClass} subclass.prototype = new SuperClass (); // первый вызов Superclass var sub1 = new subclass (); console.log (sub1.saywhat ()); //приветМетод наследования комбинации также является наиболее часто используемым способом реализации наследования в реальной разработке. На данный момент это может удовлетворить ваши реальные потребности в развитии, но стремление людей к совершенству бесконечно, поэтому в этом шаблоне неизбежно будет кто-то «найти»: ваш шаблон назывался конструктор супер-типа дважды! Два раза. Полем Полем Вы сделали это? Это усиление в 100 раз превышает потерю производительности?
Самое мощное опровержение - это решение, но, к счастью, разработчик нашел лучшее решение этой проблемы:
Паразитарная комбинация наследования
Прежде чем ввести этот метод наследования, мы сначала понимаем концепцию паразитического конструктора. Паразитный конструктор похож на заводской шаблон, упомянутый выше. Его идея состоит в том, чтобы определить общую функцию. Эта функция специально используется для обработки создания объекта. После завершения творения он возвращает этот объект. Эта функция очень похожа на конструктор, но конструктор не возвращает значение:
Функция gf (name, bra) {var obj = new Object (); obj.name = name; obj.bra = бюстгальтер; obj.saywhat = function () {console.log (this.name); } return obj;} var gf1 = new Gf ("bingbing", "c ++"); console.log (gf1.saywhat ()); // bingbingРеализация паразитического наследования аналогична паразитическому конструктору. Он создает функцию «заводской», которая не зависит от конкретных типов, в частности, касается процесса наследования объекта, а затем возвращает унаследованный экземпляр объекта. К счастью, это не требует от нас самостоятельно реализовать. Дао Ге (Дуглас) давно предоставил нам метод реализации:
функция объекта (obj) {function f () {} f.prototype = obj; вернуть новое f ();} var superclass = {name: "bingbing", bra: "c ++"} var subclass = object (superclass); console.log (subclass.name); // bingbingПростой конструктор предоставляется в публичной функции, а затем экземпляр, пройденного объекта, назначен объекту прототипа конструктора, и, наконец, возвращение экземпляра конструктора очень проста, но эффективность очень хорошая, не так ли? Этот метод позже называется «наследование прототипа», а паразитное наследование достигается на основе прототипа путем улучшения пользовательских свойств объекта:
function buildObj (obj) {var O = Object (obj); o.saywhat = function () {console.log ("hello"); } return o;} var superclass = {name: "bingbing", bra: "c ++"} var gf = buildobj (superclass); gf.saywhat (); // helloПаразитическое наследование также сталкивается с проблемой повторного использования функций в прототипах, поэтому люди снова начали собирать строительные блоки, и родилось паразитическое комбинацию наследования с целью решения проблемы вызова родительского конструктора типа при определении прототипа подтипа и в то же время, чтобы максимизировать повторное использование функции. На основе вышеуказанных методов реализации следующие:
// Параметры представляют собой две конструкторы Function inheritobj (sub, sup) {// реализация наследования экземпляра и получить копию супертипа var proto = object (sup.prototype); // Отвракайте атрибут конструктора прото -экземпляра proto.constructor = sub; // Присвоение созданного объекта на прототип подтипа sub.prototype = proto;} function superclass () {this.name = "Женщины"; this.bra = ["a", "b"];} superclass.prototype.saywhat = function () {console.log ("hello");} function subclass () {this.name = "Женщины"; this.bra = ["a", "b"];} superclass.prototype.saywhat = function () {console.log ("hello");} function subclass () {this.subname = "Ваша сестра"; Superclass.call (this);} inheritobj (subclass, superclass); var sub1 = new subclass (); console.log (sub1.saywhathhat ()); //приветЭта реализация позволяет избежать двух вызовов Supertypes, а также сохраняет ненужные свойства на подкласс. На этом этапе путешествие наследования действительно закончено, и эта реализация также стала самым идеальным методом реализации наследования! Споры людей по поводу наследства JavaScript все еще продолжаются. Некоторые защищают OO, а некоторые выступают против предложения ненужных усилий в JavaScript, чтобы реализовать характеристики ОО. Что если это так, по крайней мере, у меня есть более глубокое понимание!