JS 六种继承方式

298 阅读3分钟

预备知识:

  • 原型与原型链(__proto__、prototype、constructor)
  • Object.create()方法
  • Function.call()方法

原型链继承

function Dad(){//定义父类
	
}
Dad.prototype.age = 11;//父类原型属性即方法
Dad.prototype.sayname = function(){
	console.log(this.name)
}

function Son(name){//定义子类
	this.name = name
}
Son.prototype = new Dad();//子类原型指向父类 原型链继承重点

let son1 = new Son("Tony");//子类实例
let son2 = new Son("Jim");

son1.sayname();//Tony 继承了父类原型的方法
son2.sayname();//Jim
son1.age;//11 继承父类原型属性

借用构造函数继承

function Dad(name){//父类构造函数
	this.name = name
	this.arr = [1,2,3]
}
Dad.prototype.age = 11

function Son(name){//子类
	Dad.call(this,name)//通过call传递Son到Dad构造函数中 重点
}

let son1 = new Son("Kay");
let son2 = new Son("John");

son1.name;//Kay 继承父类构造函数中的name属性
son2.name;//John

son1.arr;//[1,2,3] 继承父类构造函数中的arr数组
son1.arr.push(1);//[1,2,3,1] 修改arr数组
son2.arr;//[1,2,3] son1对arr数组的修改不影响son2的arr数组

son1.age;//undefined 借用构造函数继承无法继承父类原型上的属性

组合继承

function Dad(name){
	this.name = name;
	this.arr = [1,2,3];
}
Dad.prototype.sayName = function(){
	console.log(this.name)
}

function Son(name){
	Dad.call(this,name)
}

Son.prototype = new Dad();

let son1 = new Son("Avery");
let son2 = new Son("Uv");

son1.sayName();//Avery 可以访问到父类原型方法
son2.sayName();//Uv

son1.arr;//[1,2,3] 
son1.arr.push(1);//[1,2,3,1] 不会改变其他实例的引用类型的值
son2.arr;//[1,2,3]

组合继承结合了原型链继承与借用构造函数继承的优点,使用原型链对方法进行继承,用构造函数对属性进行继承,从而实现方法复用,属性独立。

原型式继承

var Dad = {
	name:"Dad",
	arr:[1,2,3,1],
	sayName:function(){
		console.log(this.name)
	}
}

var Son1 = Object.create(Dad,{
	name:{
		value:'Son1'
	}
})

var Son2 = Object.create(Dad,{
	name:{
		value:'Son2'
	}
})

Son1.sayName();//Son1  调用了原型的sayName方法
Son2.sayName();//Son2
Son1.arr;//1231  原型的arr数组
Son1.arr.push(1);12311
Son2.arr;//12311 Son1对原型数组的修改也会影响到Son2

通过使用Object.create 方法将子实例的__proto__指向父亲,所以所有实例同样共享原型的属性和方法,修改其中的引用类型的值会影响到其他实例

原型式继承的好处是:如果我们只是简单的想保持一个对象和另一个对象类似,不必大费周章写一堆代码,直接调用就能实现

寄生式继承

function createObj(obj){
	let temp = function(){};
	temp.prototype = obj;
	let clone = new temp();
	
	clone.sayName = function(){
		console.log(this.name)
	}
	return clone;
}
var Dad = {
	name:"Dad",
	arr:[1,2,3]
}
let Son1 = createObj(Dad);
let Son2 = createObj(Dad);

将构建对象的过程封装进函数以返回,上述的寄生式继承方法所创建的两个实例同样共享着父类的所有属性与方法,但是各个实例也能拥有自己独立的方法比如sayName方法

寄生组合式继承

function Dad(){//父类
	//属性
}

Dad.prototype.sayName = function(){}//父类方法

function inheritPrototype(Son,Dad){
//替换掉组合式继承中的	Son.prototype = new Dad();
	let proto = Object.create(Dad.prototype);//复制父类中的原型
	proto.constructor = Son;//将复制的父类原型与子类进行连接
	Son.prototype = proto;
}

function Son(){
	Dad.call(this)
}

inheritPrototype(Son,Dad);
let son1 = new Son();


不妨看看inheritPrototype函数,他其实替换掉了我们原来的Son.prototype = new Dad(),这样就使得Son不再是Dad的一个实例,Son上面也没有Dad本身的属性与方法,但通过对Dad的原型对象进行复制并与Son连接,这样又使得Son可以继承Dad的原型方法或者属性,再结合构造函数的call方法对Dad本身的属性进行了继承,最终减少了原本在组合继承中对Dad构造函数的两次调用

这种继承方式既保留了组合继承的优点,又能避免组合继承中会有无用的属性缺陷。