js如何实现继承?六种继承模式

知识重温:

  • 每个构造函数都有一个原型对象,原型有一个属性(constructor)指回构造函数,而实例有一个内部指针_proto_([[Prototype]]) 指向原型。实例与构造函数的原型之间有直接关系,实例与构造函数之间没有。
  • 原型链的基本思想:上面如果原型是另一个类型的实例呢?意味着这个原型本身有一个内部指针指向另一个原型,相应的另一个原型也有一个指针(constructor)指向另一个构造函数。这样就在实例和原型之间构造了一条原型链.原型链还有一环,所有引用类型都继承Object,任何函数的默认原型都是一个Object实例,这就意味着这个实例有一个内部指针指向object.prototype.
    在这里插入图片描述

一.原型链继承

基本思路:通过原型继承多个引用类型的属性和方法。
缺点:
1.原型中包含的引用值会在所有实例间共享
2.子例在实例化时不能给父类型的构造函数传递参数。

function SuperType(){
	this.property  = true;
}
SuperType.prototype.getSuperValue = function (){
	return this.property;
}
function SubType (){
	this.subproperty = false;
}
//继承 SuperType
subType.prototype  =new SuperType();
SubType.prototype.getSubValue = function (){
	return this.subproperty;
}

let instance = new Subtype();
console.log(instance.getSuperValue()); //true

在这里插入图片描述

注意细节

  • 子类有时需要覆盖父类的方法,或者增加父类没有的方法,这些方法必须在原型赋值之后(即subType.prototype =new SuperType() 后)再添加到原型上。
  • 对象字面量方式创建原型方法会破坏之前的原型链,因为这相当于重写了原型链
function SuperType(){
	this.property  = true;
}
SuperType.prototype.getSuperValue = function (){
	return this.property;
}
function SubType (){
	this.subproperty = false;
}
//继承 SuperType
subType.prototype  =new SuperType();

//子类有时需要覆盖父类的方法,或者增加父类没有的方法,这些方法必须在原型赋值之后
//新方法
SubType.prototype.getSubValue = function (){
	return this.subproperty;
}
//覆盖已有方法
SubType.prototype.getSuperValue = function (){
	return false;
}

let instance = new Dubtype();
console.log(instance.getSuperValue());		 //fasle
function SuperType(){
	this.property  = true;
}
SuperType.prototype.getSuperValue = function (){
	return this.property;
}
function SubType (){
	this.subproperty = false;
}
//继承 SuperType
subType.prototype  =new SuperType();
//通过对象字面量添加新方法,这会导致上一行无效
//覆盖后原型是一个object实例,不在是superType的实例,其constructor属性也指向了完全不同的新对象(指向object构造函数)
Subtype.prototype= {
	getSubValue(){
		return this.subproperty;
	},
	someotherMethod(){
		return false;
	}
}
let instance = new Dubtype();
console.log(instance.getSuperValue()); //出错!!

二.盗用构造函数

基本思路:在子类构造函数中调用父类构造函数。因为毕竟函数就是在特定上下文中执行代码的简单对象,所以可以使用apply()和call()方法以新创建的对象为上下文执行构造函数。

缺点:
  1. 实例并不是父类的实例,只是子类的实例
  2. 只能继承父类的实例属性和方法,不能继承原型属性/方法
  3. 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

function SuperType(name){
	this.color = ['red','blue','green']
	this.name = name
}

function SubType (){
	//继承SuperType并传参
	SuperType.call(this,'我的名字')
	//实例属性
	this.age = 29;
}

let instance1 = new subType();
instance1.color.push('black')
console.log(instance1.colors);	//red ,blue green ,black
let instance2 = new subType();
console.log(instance2.colors);	//red ,blue green 

let instance = new subType();
console.log(instance.name) //	‘我的名字’
console.log(instance.age)	//29

三.组合继承

基本思路:使用原型链继承原型上的属性和方法(实现复用),而通过盗用构造函数继承实例属性(实现传参且实例相互独立)。
缺点: 创建的实例和原型上存在两份相同的属性

function Parent(name) {
  this.name = name;
  this.newArr = ["red", "blue", "green"];
}
Parent.prototype.sayName = function() {
  console.log(this.name);
};

function Child(name) {
  Parent.call(this, name);
  this.age = 26;
}

Child.prototype = new Parent();
//重写Child.prototype的constructor属性,使其执行自己的构造函数Child
Child.prototype.constructor = Child;
Child.prototype.sayAge = function() {
  console.log(this.age);
};

var Person1 = new Child("heyushuo");
console.log(Person1);
Person1.newArr.push("yellow");
console.log(Person.newArr); //["red", "blue", "green","yellow"]
Person.sayName(); //heyushuo

var Person2 = new Child("kebi");
console.log(Person2.newArr); //["red", "blue", "green"]
Person.sayName(); //kebi

在这里插入图片描述

四.原型式继承

基本思路:有一个object函数,这个函数创建一个临时构造函数,将传入的对象赋值给这个构造函数的原型,然后返回这个临时构造函数的一个实例。本质上,object()是对传入对象执行了一次浅复制。

原型式继承适合不需要单独创建构造函数,但仍需要在对象间共享信息的场所。
缺点:属性中包含的引用值始终会在相关对象间共享,跟使用原型链模式一样。

function object(o){
	function F(){};
	F.prototype = o;
	return new F();
}

等价于使用ES5的Object.create();

五.寄生式继承

基本思路:创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。
缺点:通过寄生式继承给对象 添加函数会导致函数难以重用,与构造函数类似。:跟借用构造函数模式一样,每次创建对象都会创建一遍方法。

function object(o){  //创建一个实现继承的函数  函数功能等价于使用ES5的Object.create();
	function F(){};
	F.prototype = o;
	return new F();
}



function createAnother(original){
	let clone = object(original); //通过调用函数创建一个新的对象
	clone.sayHi(){ //以某种方式增强这个对象
		console.log('hi')
	};
	return clone ; //返回这个对象
}

let person ={
	 name:'koo'
	 }
	 let anotherPerson = createAnother(person);
	 anotherPerson.sayHi() //hi


六.寄生式组合继承

基本思路:不是通过调用父类构造函数给子类原型赋值,而是取得父类原型上的一个副本。说到底就是使用寄生式继承来继承父类原型,然后将返回的新对象赋值给子类的原型。

function object(o){  //创建一个实现继承的函数
	function F(){};
	F.prototype = o;
	return new F();
}

function inherPerototype(subtype,supertype){
	subtype.prototype = object(supertype.prototype); //subtype = Object.create(supertype.prototype);
	subtype.prototype.constructor = supertype;
}
function SuperType(name){
	this.color = ['red','blue','green']
	this.name = name
}
SuperType.prototype.sayName = function() {
  console.log(this.name);
};
function SubType (name){
	SuperType.call(this,name,age)
	this.age = age;
}
inheritPrototype(subType,superType);

SubType.prototype.sayAge = function (){
	console.log(this.age)
}

猜你喜欢

转载自blog.csdn.net/HZ___ZH/article/details/110405824