JavaScript-это объектно-ориентированный язык. В JavaScript есть очень классическая поговорка: все является объектом. Поскольку он ориентирован на объект, существует три основные характеристики объектно-ориентированных: инкапсуляция, наследование и полиморфизм. Здесь мы говорим о наследстве JavaScript, и мы поговорим о двух других позже.
Наследование JavaScript не совсем такое же, как у C ++. Наследование C ++ основано на классе, в то время как наследование JavaScript основано на прототипах.
Теперь проблема здесь.
Что такое прототип? Для прототипов мы можем ссылаться на классы в C ++ и сохранить свойства и методы объекта. Например, давайте напишем простой объект
Кода -копия выглядит следующим образом:
Функция животного (имя) {
this.name = name;
}
Animal.prototype.setName = function (name) {
this.name = name;
}
var Animal = новое животное ("Вангванг");
Мы видим, что это животное объект, у которого есть имя атрибута и метод SetName. Обратите внимание, что после изменения прототипа, например, добавление метода, все экземпляры объекта поделится этим методом. Например
Кода -копия выглядит следующим образом:
Функция животного (имя) {
this.name = name;
}
var Animal = новое животное ("Вангванг");
В настоящее время у животного есть только атрибут названия. Если мы добавим предложение,
Кода -копия выглядит следующим образом:
Animal.prototype.setName = function (name) {
this.name = name;
}
В настоящее время у животного также будет метод SetName.
Копия наследования - Начиная с пустого объекта, мы знаем, что среди основных типов JS существует тип, называемый объектом, а его основным экземпляром является пустой объект, то есть экземпляр, генерируемый вызовом New Object () напрямую или объявлен с буквальным {}. Пустой объект - это «чистый объект», с только предопределенными свойствами и методами, в то время как все другие объекты наследуются от пустых объектов, поэтому все объекты имеют эти предопределенные свойства и методы. Прототип на самом деле является экземпляром объекта. Значение прототипа заключается в том, что у конструктора есть прототип объект A, экземпляры, созданные конструктором, должны быть скопированы из A. Поскольку экземпляр копируется из объекта A, экземпляр должен наследовать все свойства, методы и другие свойства A. Так, как реализуется репликация? Метод 1: Создайте копирование каждого построенного экземпляра, копируется из прототипа, а новый экземпляр занимает то же пространство памяти, что и прототип. Хотя это делает OBJ1 и OBJ2 совершенно согласованными с их прототипами, он также очень неэкономичный - потребление пространства памяти быстро увеличится. Как показано на картинке:
Метод 2: Скопируйте на написании этой стратегии технология постоянной системы обмана: копия на написании. Типичным примером такого рода мошенничества является библиотека динамических ссылок (DDL) в операционной системе, область памяти которого всегда копируется на записи. Как показано на картинке:
Нам просто нужно указать в системе, что OBJ1 и OBJ2 эквивалентны их прототипам, поэтому при чтении нам нужно только следовать инструкциям для чтения прототипа. Когда нам нужно написать свойства объекта (например, OBJ2), мы копируем прототип изображения и указываем последующие операции на изображение. Как показано на картинке:
Преимущество этого метода заключается в том, что нам не нужно много накладных расходов памяти при создании экземпляров и чтения атрибутов. Мы используем какой -то код для распределения памяти только при первом написании, и приводим код и накладные расходы на память. Но с тех пор таких накладных расходов не будет, потому что эффективность доступа к изображениям и доступа к прототипам является последовательной. Однако для систем, которые часто пишут операции, этот метод не является экономичным, чем предыдущий метод. Метод 3: Чтение Траверсии Этот метод превращает гранулярность копии из прототипа к члену. Этот метод характеризуется копированием информации о участнике в изображение экземпляра только при написании члена экземпляра. Например, при написании свойств объекта (obj2.value = 10) будет сгенерировано именованное значение атрибута и помещено в список элементов объекта obj2. Посмотрите на картинку:
Можно обнаружить, что OBJ2 по -прежнему является ссылкой на прототип, и никакие экземпляры объектов того же размера, что и прототип, не были созданы во время операции. Таким образом, операции записи не приводят к большому количеству распределения памяти, поэтому использование памяти кажется экономичным. Разница в том, что OBJ2 (и все экземпляры объекта) необходимо поддерживать список участников. Этот список участников следует двум правилам: к нему гарантированно доступ к ней сначала при чтении. Если в объекте не указано атрибут, попробуйте пересечь всю цепь прототипа объекта, пока прототип не станет пустым или не будет найдено свойство. Цепочка прототипа будет обсуждена позже. Очевидно, что среди трех методов чтение Traversal является лучшей производительности. Поэтому прототип прототипа JavaScript читается. Люди конструктора, которые знакомы с C ++, определенно будут запутаны после прочтения кода верхнего объекта. Это легко понять без ключевого слова класса, в конце концов, есть ключевые слова функции, но ключевые слова разные. Но как насчет конструктора? На самом деле, JavaScript также имеет аналогичные конструкторы, но они называются конструкторами. При использовании нового оператора был вызван конструктор, и это связано как объект. Например, мы используем следующий код
Кода -копия выглядит следующим образом:
var Animal = Animal ("wangwang");
Животное будет неопределенным. Некоторые люди скажут, что никакая возвратная ценность, конечно, не является неопределенной. Затем, если вы измените определение объекта животного:
Кода -копия выглядит следующим образом:
Функция животного (имя) {
this.name = name;
вернуть это;
}
Угадайте, что сейчас животное?
В настоящее время животное стало окном. Разница в том, что он расширяет окно, так что у окна есть атрибут имени. Это связано с тем, что это по умолчанию в окно, то есть переменная верхнего уровня без указания ее. Только призыв к новому ключевому слову можно правильно вызвать конструктор. Итак, как избежать нового ключевого слова, пропущенного человеком, использующим его? Мы можем внести некоторые незначительные изменения:
Кода -копия выглядит следующим образом:
Функция животного (имя) {
if (! (Этот экземпляр животного)) {
вернуть новое животное (имя);
}
this.name = name;
}
Это будет надежно. Конструктор также имеет другое использование, указывая, к какому объекту принадлежит экземпляр. Мы можем использовать экземпляр, чтобы судить, но экземпляр вернуть истину в предки объектов и реальных объектов при наследстве, поэтому он не очень подходит. Когда конструктор называется новым, он по умолчанию указывает на текущий объект.
Кода -копия выглядит следующим образом:
console.log (Animal.Prototype.constructor === Animal); // истинный
Мы можем думать по -другому: прототип не является ценным в начале функции, и реализация может быть следующей логикой
// set __proto__ является встроенным элементом функции, get_prototyoe () является его методом
Кода -копия выглядит следующим образом:
var __proto__ = null;
функция get_prototype () {
if (! __ proto__) {
__proto__ = новый объект ();
__proto __. Constructor = this;
}
вернуть __proto__;
}
Это преимущество заключается в том, что он избегает создания экземпляра объекта для каждой объявленной функции, сохраняя накладные расходы. Конструктор может быть изменен, что будет обсуждаться позже. Я считаю, что все знают, что наследство основано на прототипах, поэтому они не демонстрируют свой нижний предел IQ.
Есть несколько типов наследства JS, вот два типа
1. Метод 1 Этот метод чаще всего используется и имеет лучшую безопасность. Давайте сначала определим два объекта
Кода -копия выглядит следующим образом:
Функция животного (имя) {
this.name = name;
}
Функция собаки (возраст) {
this.age = возраст;
}
var dog = новая собака (2);
Очень просто построить наследование, указать прототип объекта дочернего объекта на экземпляр родительского объекта (обратите внимание, что это экземпляр, а не объект)
Кода -копия выглядит следующим образом:
Dog.prototype = New Animal ("wangwang");
В настоящее время у собаки будет два атрибута, имя и возраст. И если экземпляр оператора используется для собаки
Кода -копия выглядит следующим образом:
Console.log (экземпляр собаки животного); // истинный
console.log (собачья экземпляр собаки); // ЛОЖЬ
Это достигает наследства, но есть небольшая проблема
Кода -копия выглядит следующим образом:
console.log (dog.prototype.constructor === Animal); // истинный
console.log (dog.prototype.constructor === Dog); // ЛОЖЬ
Вы можете видеть, что объект, на который указал конструктор, изменился, что не соответствует нашей цели. Мы не можем определить, к чему принадлежат к нему экземпляр. Поэтому мы можем добавить одно предложение:
Кода -копия выглядит следующим образом:
Dog.prototype.constructor = Dog;
Давайте снова посмотрим:
Кода -копия выглядит следующим образом:
Console.log (экземпляр собаки животного); // ЛОЖЬ
console.log (собачья экземпляр собаки); // истинный
сделанный. Этот метод является ссылкой в поддержании цепочки прототипа, которая будет подробно объяснена ниже. 2. Метод 2 Этот метод имеет свои преимущества и недостатки, но недостатки перевешивают преимущества. Сначала посмотрите на код
Кода -копия выглядит следующим образом:
<pre name = "code"> function Animal (name) {
this.name = name;
}
Animal.prototype.setName = function (name) {
this.name = name;
}
Функция собаки (возраст) {
this.age = возраст;
}
Dog.prototype = Animal.prototype;
Это позволяет копировать прототип.
Преимущество этого метода заключается в том, что он не требует экземпляров объектов (по сравнению с методом 1), сохранения ресурсов. Недостатки также очевидны. В дополнение к той же проблеме, что и выше, то есть конструктор указывает на родительский объект, он может только копировать свойства и методы, объявленные родительским объектом с помощью прототипа. То есть в приведенном выше коде атрибут имени объекта животного не может быть скопирован, но метод SETNAME может быть скопирован. Самым фатальным является то, что любая модификация прототипа дочернего объекта будет влиять на прототип родительского объекта, то есть на случаи, объявленные обоими объектами. Поэтому этот метод не рекомендуется.
Прототип цепочка
Любой, кто писал о наследстве, знает, что наследство может быть унаследовано с нескольких уровней. И в JS это образует цепочку прототипа. В приведенной выше статье также упоминалась цепочка прототипа много раз, что такое цепочка прототипа? Экземпляр должен, по крайней мере, иметь атрибут прото, указывающий на прототип, который является основой объектной системы в JavaScript. Однако это свойство невидимо, и мы называем ее «внутренней цепочкой прототипа», чтобы отличить ее от «цепочки прототипа конструктора», состоящей из прототипа конструктора (то есть то, что мы обычно называем «цепью прототипов»). Давайте сначала построим простые отношения наследования в соответствии с приведенным выше кодом:
Кода -копия выглядит следующим образом:
Функция животного (имя) {
this.name = name;
}
Функция собаки (возраст) {
this.age = возраст;
}
var Animal = новое животное ("Вангванг");
Dog.prototype = животное;
var dog = новая собака (2);
В качестве напоминания, как упоминалось ранее, все объекты наследуют пустые объекты. Итак, мы строим цепочку прототипа:
Мы видим, что прототип дочернего объекта указывает на экземпляр родительского объекта, образуя цепочку прототипа конструктора. Внутренний прото -объект экземпляра ребенка также является экземпляром, указывающим на родительский объект, образуя внутреннюю цепочку прототипа. Когда нам нужно найти свойство, код похож на
Кода -копия выглядит следующим образом:
function getattrfromobj (attr, obj) {
if (typeof (obj) === "Object") {
var proto = obj;
while (proto) {
if (proto.hashownproperty (attr)) {
вернуть прото [attr];
}
proto = proto .__ Proto__;
}
}
вернуть неопределенные;
}
В этом примере, если мы ищем атрибут имени у собаки, он будет искать в списке участников в собаке. Конечно, это не будет найдено, потому что теперь список членов собаки - только возраст предмета. Затем он будет продолжать искать вдоль цепочки прототипа, то есть экземпляр, на который указывает .proto, то есть в животном, найдите атрибут имени и верните его. Если поиск - это свойство, которого не существует, когда его нельзя найти в животном, он будет продолжать искать с .proto, найти пустой объект, а затем продолжать искать с .proto, а .proto пустого объекта указывает на нулевый, ища выход.
Поддержание прототипов цепочек мы подняли вопрос, когда говорили о прототипе наследования только сейчас. При использовании метода 10 для построения наследования конструктор экземпляра объекта дочернего объекта указывает на родительский объект. Преимущество этого заключается в том, что мы можем получить доступ к цепочке прототипа через атрибут конструктора, и недостатки также очевидны. Объект, экземпляр которого должен указывать на себя, т.е.
Кода -копия выглядит следующим образом:
(новый obj ()). Prototype.constructor === obj;
Затем, после того, как мы переписываем свойства прототипа, конструктор экземпляра, сгенерированного дочерним объектом, не указывает на себя! Это идет вразрез с первоначальным намерением конструктора. Мы упомянули решение выше:
Кода -копия выглядит следующим образом:
Dog.prototype = New Animal ("wangwang");
Dog.prototype.constructor = Dog;
Кажется, что нет ничего плохого. Но на самом деле это поднимает новую проблему, потому что мы обнаружим, что мы не можем вернуться к цепочке прототипа, потому что мы не можем найти родительский объект, а атрибут .proto внутренней цепи прототипа недоступен. Поэтому Spidermonkey обеспечивает улучшение: добавьте свойство с именем __proto__ в любой созданный объект, который всегда указывает на прототип, используемый конструктором. Таким образом, любая модификация конструктора не повлияет на значение __proto__, что делает его удобным для поддержания конструктора.
Однако есть еще две проблемы:
__proto__ переписан, что означает, что при его использовании все еще существуют риски
__proto__ является специальной обработкой Spidermonkey и не может использоваться в других двигателях (таких как JScript).
У нас есть другой способ поддерживать свойства конструктора прототипа и инициализировать свойства конструктора экземпляра в функции конструктора подкласса.
Код выглядит следующим образом: Перепишите дочерний объект
Кода -копия выглядит следующим образом:
Функция собаки (возраст) {
this.constructor = Arguments.callee;
this.age = возраст;
}
Dog.prototype = New Animal ("wangwang");
Таким образом, конструктор всех экземпляров детских объектов правильно указывает на объект, в то время как конструктор прототипа указывает на родительский объект. Хотя этот метод является относительно неэффективным, поскольку атрибут конструктора должен быть переписан каждый раз, когда построен экземпляр, нет никаких сомнений в том, что этот метод может эффективно разрешить предыдущие противоречия. ES5 учитывает это и полностью решает эту проблему: object.getPrototypeof () может использоваться в любое время для получения реального прототипа объекта без доступа к конструктору или поддержанию внешней цепочки прототипа. Поэтому, как упоминалось в предыдущем разделе, мы можем переписать его следующим образом:
Кода -копия выглядит следующим образом:
function getattrfromobj (attr, obj) {
if (typeof (obj) === "Object") {
делать {
var proto = object.getPrototypeof (dog);
if (proto [attr]) {
вернуть прото [attr];
}
}
в то время как (прото);
}
вернуть неопределенные;
}
Конечно, этот метод может использоваться только в браузерах, которые поддерживают ES5. Чтобы быть обратной совместимостью, нам все еще нужно рассмотреть предыдущий метод. Более подходящий метод - интегрировать и инкапсулировать эти два метода. Я считаю, что читатели очень хороши в этом, поэтому я не буду здесь уродством.