Выберите способ наследования
JavaScript — динамический язык, а динамичность означает высокую гибкость, и это может отразиться, в частности, на наследовании. Существует множество способов реализации наследования в JavaScript, которые можно разделить на следующие четыре категории:
- Режим Mixin, то есть миксин свойств, копирует свойства из одного или нескольких объектов в новый объект.
- Режим заимствования метода, то есть повторное использование метода посредством вызова или применения.
- Режим прототипа: используйте метод Object.create для непосредственного создания нового объекта с объектом в качестве прототипа.
- Шаблон класса, фактически использующий конструктор или класс ES6
Первые три имеют одну общую черту, то есть нет понятия «класс», они очень полезны в соответствующих сценариях, но поскольку класса нет, многие элементы классического объектно-ориентированного наследования отсутствуют. Например, между родительскими и дочерними объектами нет строгой связи наследования, то есть это не обязательно связь «есть-а», которая определяет, что они не могут быть непосредственно применены к объектно-ориентированному анализу и проектированию. являются не истинным наследованием, а между схемой повторного использования кода между наследованием и композицией.
Четвертый тип, наследование на основе классов, может выражать четкие отношения наследования независимо от того, используется ли конструктор или класс, добавленный ES 6. В сценарии, где необходимо активно использовать наследование, следует использовать наследование на основе классов. Далее в этой статье обсуждается наследование классов.
Следует иметь в виду одну вещь: наследование — это форма сильной связи, и его следует использовать с осторожностью.
Как изучить наследование в JavaScript
Я думаю, что лучший способ понять реализацию наследования классов в JavaScript — это найти язык с более полным объектно-ориентированным механизмом для понимания наследования. На самом деле все новые классы и классы ES6, добавленные в JavaScript, являются отсылками к языку Java.
Однако такое сравнительное исследование является обязательным условием, то есть сначала освоить прототип, цепочку прототипов и область видимости в JavaScript, иначе легко неправильно понять механизм выполнения сути JavaScript. Если вы уже понимаете эти предварительные условия, вы можете изучить наследование в JavaScript.
Наследование с ES3
Точки реализации:
- Используйте Person.call(this) для выполнения «заимствования метода», чтобы получить свойства Person.
- Добавьте Person.prototype в цепочку прототипов с пустой функцией
function Person(name) {
this.name = name;
}
Person.prototype.printName = function() {
console.log(this.name);
};
function Bob() {
Person.call(this, "Bob");
this.hobby = "Histroy";
}
function inheritProto(Parent, Child) {
var Fn = function() {};
Fn.prototype = Parent.prototype;
Child.prototype = new Fn();
Child.prototype.constructor = Child;
}
inheritProto(Person, Bob);
Bob.prototype.printHobby = function() {
console.log(this.hobby);
};
console.dir(new Bob());
реж выход:
Bob
|-- hobby:"Histroy"
|-- name:"Bob"
|-- __proto__:Person
|-- printHobby:ƒ ()
|-- constructor:ƒ Bob()
|-- __proto__:
|-- printName:ƒ ()
|-- constructor:ƒ Person(name)
|-- __proto__:Object
Наследование с ES5
Точки реализации:
- Используйте Person.call(this) для выполнения «заимствования метода», чтобы получить свойства Person.
- Используйте метод Object.create, добавленный ES5, чтобы добавить Person.prototype в цепочку прототипов.
function Person(name) {
this.name = name;
}
Person.prototype.printName = function() {
console.log(this.name);
};
function Bob() {
Person.call(this, "Bob");
this.hobby = "Histroy";
}
Bob.prototype = Object.create(Person.prototype, {
constructor: {
value: Bob,
enumerable: false,
configurable: true,
writable: true
}
});
Bob.prototype.printHobby = function() {
console.log(this.hobby);
};
console.dir(new Bob());
реж выход:
Bob
|-- hobby:"Histroy"
|-- name:"Bob"
|-- __proto__:Person
|-- printHobby:ƒ ()
|-- constructor:ƒ Bob()
|-- __proto__:
|-- printName:ƒ ()
|-- constructor:ƒ Person(name)
|-- __proto__:Object
Наследование с ES6
Точки реализации:
- Используйте добавленные классы ES6 и распространяются на достижение лучшего наследства, чем когда-либо прежде
class Person {
constructor(name) {
this.name = name;
}
printName() {
console.log(this.name);
}
}
class Bob extends Person {
constructor() {
super("Bob");
this.hobby = "Histroy";
}
printHobby() {
console.log(this.hobby);
}
}
console.dir(new Bob());
реж выход:
Bob
|-- hobby:"Histroy"
|-- name:"Bob"
|-- __proto__:Person
|-- constructor:class Bob
|-- printHobby:ƒ printHobby()
|-- __proto__:
|-- constructor:class Person
|-- printName:ƒ printName()
|-- __proto__:Object
Наследование между JavaScript и Java с точки зрения класса и супер
При написании кода два наиболее очевидных удобства, которые приносят классы ES6:
- Скрыть процесс объединения цепочки прототипов и сфокусировать код на наследовании между типами.
- Используйте super для более простых и гибких полиморфных методов.
На самом деле, ES6 добавил много новых функций, связанных с классом, таких как наследование, которое отличается от того, что было раньше: наследование, реализованное с классом, включает в себя как отношения наследования экземпляров класса, так и отношения наследования самого класса. Класс здесь на самом деле является специальной функцией JavaScript, а в JavaScript функция — это подтип объекта, то есть объект функции, поэтому он также может отражать прототипное наследование.
Например, используя предыдущий код для иллюстрации:
// 类实例的继承关系
Bob.prototype.__proto__ === Person.prototype // true
// 类本身的继承关系
Bob.__proto__ === Person // true
Давайте посмотрим на super в ES6. Когда метод подкласса хочет использовать метод родительского класса для выполнения части работы, super может пригодиться. Это более тонкое повторное использование кода, чем наследование, но связь также стала сильнее. На самом деле у super тоже есть много функций, которые можно использовать как функции и объекты. Сочетая class и super, вы можете понять сходства и различия между JavaScript и Java в наследовании.
То же самое или очень похожее на Java:
- Вызвать конструктор суперкласса в конструкторе подкласса. В ES6 конструктор подкласса должен вызвать конструктор родительского класса для завершения инициализации, а экземпляр подкласса обрабатывается на основе экземпляра родительского класса. Именно по этой причине все поведения родительского класса могут быть унаследованы. Следовательно, ES6 может наследовать полные функции собственной структуры данных и определять на этой основе свою собственную структуру данных. Подобно наследованию класса HashMap в Java, JavaScript может наследовать такие конструкторы, как Number и Array.
В отличие от Java:
- В обычных методах super может вызывать метод для объекта-прототипа родительского класса (что можно понимать как super, указывающее на объект-прототип родительского класса в это время); в статических методах super может вызывать статический метод объекта-прототипа родительского класса. родительский класс (который можно понимать как супер теперь указывает на родительский класс). В Java вы можете использовать super для доступа к переопределенной переменной или методу с тем же именем в родительском классе.Для доступа к статическим методам вы можете использовать «имя класса.имя метода» или «имя объекта.имя метода».
После сравнения видно, что он действительно очень похож на Java.
В сочетании с предыдущим содержанием можно обнаружить, что от ES3 до ES6 объектно-ориентированная часть JavaScript приближается к Java. Особенно после добавления класса и ключевых слов extends это большой шаг вперед. Но это не меняет сути того, что JavaScript основан на прототипах. Класс в Java подобен диаграмме проекта объекта.Каждый раз, когда для создания нового объекта вызывается новый объект, создается независимый объект, занимающий независимое пространство памяти, в то время как в JavaScript работа наследования на самом деле заключается в создании объекта. цепочка прототипов Экземпляры подклассов имеют один и тот же прототип. Таким образом, вызов метода родительского класса в JavaScript фактически вызывает тот же метод для другого объекта, то есть «заимствование метода», что на самом деле является вызовом «делегирования».