Наследование JavaScript

JavaScript
Наследование JavaScript

Наследование цепочки прототипов

принцип

Суть в томПереопределить объект-прототип, замените его экземпляром нового типа. В следующем коде оригинал существует вSuperTypeСвойства и методы экземпляра объекта теперь также существуют вSubType.prototypeбинго.

выполнить

function Super(){
    this.value = true;
}
Super.prototype.getValue = function(){
    return this.value
}
function Sub(){};
// Sub继承了Super
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;

const ins = new Sub();
console.log(ins.getValue()); // true

SubнаследоватьSuper, в то время как наследование создается путем созданияSuperэкземпляр и назначьте экземплярSub.prototypeосуществленный. изначально существовал вSuperВсе свойства и методы в экземпляре теперь также существуют сSub.prototypeсередина. как показано на рисунке.

Как видно из приведенного выше рисунка, бесполезноSubПрототип предоставляется по умолчанию, но заменен новым прототипом; этот новый прототипSuperпример. Таким образом, новый прототип не только имеет в качествеSuperсвойств и методов, принадлежащих экземпляру , а также указывает наSuperПрототип. Конечный результат таков:

ins=>Sub的原型=>Super的原型

getValue()метод по-прежнемуSub.prototypeв, ноvalueнедвижимость находится вSub.prototypeсередина. Это потому чтоvalueявляется свойством экземпляра, иgetValue()является методом-прототипом. теперь, когдаSub.prototypeсейчасSuperнапример, тогдаvalueв этом случае.

Также обратите вниманиеins.constructorтеперь указывает наSuper, это потому что оригинал Sub.prototypeКонструктор in переопределен.

недостаток

  • Частные свойства прототипа совместно используются экземплярами
  • При создании экземпляра подтипа нельзя передавать аргументы конструктору супертипа

Основная проблема с наследованием цепочки прототипов:Частные свойства прототипа совместно используются экземплярами, поэтому вы определяете свойства в конструкторе, а не в объекте-прототипе. При реализации наследования через прототипыЭкземпляр прототипа становится экземпляром другого класса. В результате исходный атрибут экземпляра стал текущим атрибутом прототипа.

function Super(){
    this.colors = ['red','green','blue'];
}
Super.prototype.getValue = function(){
    return this.colors
}
function Sub(){};
//Sub继承了Super
Sub.prototype = new Super();
const ins1 = new Super();
ins1.colors.push('black');
console.log(ins1.colors);//['red','green','blue','black'];
const ins2 = new Sub();
console.log(ins2.colors);//['red','green','blue','black'];

цепь прототиповВторая проблема, при создании экземпляра подтипа нельзя передавать параметры в конструктор супертипа. На самом деле следует сказать, что нет способа передать параметры конструктору супертипа, не затрагивая все нужные экземпляры. В сочетании с проблемой того, что свойства прототипа, содержащие значения ссылочного типа, будут общими для всех экземпляров, наследование цепочки прототипов редко используется на практике отдельно.

Обратите внимание на проблему

Используйте метод наследования цепочки прототипов для тщательного определения методов.Иногда подтипам необходимо переопределить метод родительского класса или добавить метод, которого нет в родительском классе. Однако код, добавляющий методы к прототипу, должен располагаться после оператора, заменяющего прототип.

function Super() {
    this.colors = ['red', 'green', 'blue'];
}
Super.prototype.getValue = function() {
    return this.colors
}

function Sub() {
    this.colors = ['black'];
};
//Sub继承了Super
Sub.prototype = new Super();

//添加父类已存在的方法,会重写父类的方法
Sub.prototype.getValue = function() {
    return this.colors;
}
//添加父类不存在的方法
Sub.prototype.getSubValue = function(){
    return false;
}
const ins = new Sub();
//重写父类的方法之后得到的结果
console.log(ins.getValue()); //['black']
//在子类中新定义的方法得到的结果
console.log(ins.getSubValue());//false
//父类调用getValue()方法还是原来的值
console.log(new Super().getValue());//['red', 'green', 'blue']

Заимствование наследования конструктора

принцип

Заимствованные конструкторы (иногда называемые наследованием псевдоклассов или классическим наследованием). Основная идея этой техники довольно проста, а именноВызов конструктора родительского класса внутри конструктора дочернего класса. Не забывайте, что функции — это не что иное, как объекты, выполняющие код в определенном контексте, поэтому с помощьюapply()а такжеcall()Методы также могут выполнять конструкторы вновь созданных объектов.

выполнить

function Super() {
    this.colors = ['red', 'green', 'blue'];
}
Super.prototype.getValue = function(){
    return this.colors;
}
function Sub(){
//继承了Super
Super.call(this);//相当于把构造函数Super中的this替换成了ins实例对象,这样在Super只有定义的私有属性会被继承下来,原型属性中定义的公共方法不会被继承下来
}
const ins = new Sub();
console.log(ins.colors);

Передача параметров: по сравнению с цепочкой прототипов наследование заимствования конструктора имеет большое преимущество, то есть вы можете передавать параметры конструктору родительского класса в конструкторе подкласса.

function B(name){
    this.name = name;
}
function A(){
    //继承了B,同时还传递了参数
    B.call(this,'ZZ');
    //实例属性
    this.age = 100;
}
const p = new A();
alert(p.name);//'ZZ'
alert(p.age);//100

недостаток

Если вы просто позаимствоваете конструктор, вам не удастся избежать проблем шаблона конструктора — методы определяются в конструкторе, поэтому повторное использование функции невозможно. Более того, методы, определенные в прототипе родительского класса, невидимы для дочернего класса, поэтому этот метод используется реже.

наследование композиции

принцип

Комбинаторное наследование относится к шаблону наследования, который сочетает в себе методы цепочки прототипов и заимствованных конструкторов, чтобы использовать их сильные стороны. Идея заключается в том, чтобы использовать цепочку прототипов для реализации наследования общедоступных свойств и методов прототипа, а также для реализации наследования частных свойств родительского класса путем заимствования наследования конструктора. Таким образом, повторное использование функций реализуется путем определения методов прототипа родительского класса, и можно гарантировать, что каждый экземпляр будет иметь частные свойства родительского класса.

выполнить

function Super(name){
    this.name = name;
    this.colors = ['red','blue','green'];
}
Super.prototype.sayName = function(){
    alert(this.name);
}
function Sub(name,age){
    Super.call(this,name);
    this.age = age;
}
// 继承方法
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
Sub.prototype.sayAge = function(){
    alert(this.age);
}
const ins = new Sub('jarvis',18);
ins.colors.push('black');
console.log(ins.colors);// ["red", "blue", "green", "black"]
ins.sayName();//'jarvis'
ins.sayAge();//18

const ins2 = new Sub('ershiyi',21);
console.log(ins2.colors);//["red", "blue", "green"]
ins2.sayName();//'ershiyi'
ins2.sayAge();//21

В предыдущем примереSubКонструктор определяет два свойства:nameа такжеage.SuperПрототип определяетsayName()метод. существуетSubвызвать конструкторSuperКонструктор передает параметр name, а затем определяет свой собственный возраст атрибута. потомSuperЭкземпляр назначаетсяSub, а затем определить методы для этого нового прототипаsayAge(). Таким образом, разныеSubЭкземпляры имеют свои собственные свойства, в том числе свойство colors, и могут использовать один и тот же метод для комбинирования наследования, чтобы избежать дефектов цепочек прототипов и заимствованных конструкторов, и объединить их преимущества, что называется наиболее часто используемым шаблоном наследования в JavaScript.

недостаток

В любом случае конструктор суперкласса вызывается дважды: один раз при создании прототипа подкласса и один раз внутри конструктора подкласса.

Паразитическая композиционная наследственность

принцип

Композиционное наследование является наиболее часто используемым шаблоном наследования в JavaScript, однако оно имеет свои недостатки. Самая большая проблема с наследованием композиции заключается в том, что в любом случае конструктор родительского класса вызывается дважды: один раз при создании прототипа подкласса и один раз внутри конструктора подкласса. Да, подтип в конечном итоге будет содержать все свойства экземпляра объекта супертипа, но ему придется переопределить эти свойства при вызове конструктора подтипа. Взгляните на следующий пример композиционного наследования.

выполнить

function Super(name){
    this.name = name;
    this.colors = ['red','blue','green'];
}
Super.prototype.sayName = function(){
    alert(this.name);
}
function Sub(name,age){
    Super.call(this,name);
    this.age = age;
}
// 继承方法
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
Sub.prototype.sayAge = function(){
    alert(this.age);
}
const ins = new Sub('jarvis',18);
ins.colors.push('black');
console.log(ins.colors);// ["red", "blue", "green", "black"]
ins.sayName();//'jarvis'
ins.sayAge();//18

const ins2 = new Sub('ershiyi',21);
console.log(ins2.colors);//["red", "blue", "green"]
ins2.sayName();//'ershiyi'
ins2.sayAge();//21

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

function Super(name){
    this.name = name;
    this.colors = ['red','blue','green'];
}
Super.prototype.sayName = function(){
    alert(this.name);
}
function Sub(name,age){
    //继承实例属性
    Super.call(this,name);
    this.age = age;
}
// 继承公有的方法
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
Sub.prototype.sayAge = function(){
    alert(this.age);
}
const ins = new Sub('jarvis',18);
ins.colors.push('black');
console.log(ins.colors);// ["red", "blue", "green", "black"]
ins.sayName();//'jarvis'
ins.sayAge();//18

const ins2 = new Sub('ershiyi',21);
console.log(ins2.colors);//["red", "blue", "green"]
ins2.sayName();//'ershiyi'
ins2.sayAge();//21

множественное наследование

В JavaScript нет множественного наследования, что означает, что объект не может наследовать несколько объектов одновременно, но этого можно добиться с помощью обходных путей.

<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title>18 多重继承</title>
</head>
<body>
	<script type="text/javascript">
	// 多重继承:一个对象同时继承多个对象
	// Person  Parent  Me
	function Person(){
		this.name = 'Person';
	}
	Person.prototype.sayName = function(){
		console.log(this.name);
	}
	// 定制Parent
	function Parent(){
		this.age = 30;
	}
	Parent.prototype.sayAge = function(){
		console.log(this.age);
	}
	
	function Me(){
		// 继承Person的属性
		Person.call(this);
		Parent.call(this);
	}
	// 继承Person的方法
	Me.prototype = Object.create(Person.prototype);
	// 不能重写原型对象来实现 另一个对象的继承
	// Me.prototype = Object.create(Parent.prototype);
	// Object.assign(targetObj,copyObj)
	Object.assign(Me.prototype,Parent.prototype);
	
	// 指定构造函数
	Me.prototype.constructor = Me;
	const me = new Me();
	
	</script>
</body>
</html>

Различия в наследовании ES5 и ES6

В традиционном наследовании ES5 значение this создается производным классом до вызова конструктора базового класса. Это означает, что это начинается как экземпляр производного класса,

Затем он украшается дополнительными свойствами базового класса.

В наследовании на основе классов ES6 значение this создается базовым классом перед изменением конструктором производного класса. В результате он изначально обладает всеми функциями встроенного объекта в качестве базового класса и правильно получает все связанные с ним функции.