js继承是我们经常在面试中遇到的问题,他的方法又有很多,我们很容易搞混或者是记混,今天我们就来回顾一下js的继承方式,以及各种方式的优缺点。
1,原型链继承
子类构造函数的原型为父类构造函数的实例对象。
缺点:1,子类构造函数无法向父类构造函数传参。
2,所有的子类实例共享着一个原型对象,一旦原型对象的属性发生改变,所有子类的实例对象都会受到影响。
3,如果要给子类的原型上添加方法,必须放在son.prototype = new Father()语句后面。
function Father(name){
this.name = name
}
Father.prototype.showName = function(){
console.log(this.name)
}
function Son(age){
this.age = 30
}
// 原型链继承,将子函数的原型绑定到复函数的实例上,子函数可以通过原型链找到父函数的原型,实现继承
Son.prototype = new Father()
// 将Son原型的构造函数指回Son,否则Son实例的constructor会指向Father
Son.prototype.constructor = Son
Son.prototype.showAge = function(){
console.log(this.age)
}
let son = new Son(40, '苹果') // 无法向父构造函数里传参
//子类构造函数的实例继承了父类构造函数原型的属性,所以无法访问到父类构造函数原型里的showAge方法
// 子类构造函数的实例继承了父类构造函数的属性,但是无法传参赋值,所以this.name是undefined
son.showName() // undefined
son.showAge() // 40
2,构造函数继承
用call()或者apply()方法,在子类构造函数中,调用父类构造函数中,调用父类构造函数
缺点:1,只继承了父类构造函数的属性,没有继承父类原型的属性。
2,无法实现函数复用,如果父类构造函数里面有一个方法,会导致每一个子类实力上面都有相同的方法。
function Father(name){
this.name = name
}
Father.prototype.showName = function(){
console.log(this.name)
}
function Son(name, age){
Father.call(this, name) //在son中借用了Father函数,只继承了父类构造函数的属性,没有继承父类原型的属性。
// 相当于this.name = name
this.age = age
}
let a = new Son(40, '梨子')
console.log(a.name) //梨子
console.log(a.showName) // undefined
3,组合继承
原型链继承+构造函数继承
缺点:使用组合继承时,父类构造函数会被调用两次,子类实例对象与子类的原型上会有相同的方法与属性,浪费内存。
function Father(name){
this.name = name;
this.say = function(){
console.log('hi, apple')
}
}
Father.prototype.showName = function(){
console.log(this.name)
}
function Son(name, age){
Father.call(this,name)
this.age = age
}
//原型链继承
Son.prototype = new Father() //son实例的原型上,会有同样的属性,父类构造函数相当于调用了两次
//将Son原型的构造函数指回son,否则实例的constructor会指向Father
Son.prototype.constructor = Son
Son.prototype.showAge = function(){
console.log(this.age)
}
let p = new Son('菠萝',40) //可以向父类构造函数传参
// 也继承了父函数原型上的方法
console.log(p)
p.showName() // '菠萝'
p.showAge() // 40
4,原型式继承
创建一个函数,将要继承的对象通过参数传递给这个函数,最终返回一个对象,他的隐式原型指向传入的对象。(Object.create()方法的底层就是原型式继承)
缺点:只能继承父类函数原型对象上的属性和方法,无法给父类构造函数传参。
function createObj(obj){
function F(){} //声明一个构造函数
F.prototype = obj // 将这个构造函数的原型指向传入的对象
F.prototype.constructor = F // constructor属性指回构造函数
return new F //返回子类构造函数的实例
}
function Father (){
this.name = '哈密瓜'
}
Father.prototype.showName = function(){
console.log(this.name)
}
const son = createObj(Father.prototype)
son.showName() // undefined 继承了原型上的方法,但是没有继承构造函数里的name属性
5,寄生式继承
在原型式继承的函数里,给继承的对象添加属性和方法,增强这个对象。
缺点:只能继承父类函数原型对象上的属性和方法,无法给父类构造函数传参。
function createObj(obj){
function F(){} //声明一个构造函数
F.prototype = obj // 将这个构造函数的原型指向传入的对象
F.prototype.constructor = F // constructor属性指回构造函数
F.prototype.age = 30
F.prototype.showAge = function(){
console.log(this.age)
}
return new F //返回子类构造函数的实例
}
function Father (){
this.name = '哈密瓜'
}
Father.prototype.showName = function(){
console.log(this.name)
}
const son = createObj(Father.prototype)
son.showName() // undefined 继承了原型上的方法,但是没有继承构造函数里的name属性
son.showAge() // 30
6,寄生组合继承
原型式继承+构造函数继承
js最佳的继承方式,只调用了父类构造函数一次。
function Father(name){
this.name = name
this.say = function(){
console.log('hi, pear')
}
}
Father.prototype.showName = function(){
console.log(this.name)
}
function Son(name, age){
Father.call(this, name)
this.age = age
}
Son.prototype = Object.create(Father.pprototype) // object.create方法返回一个对象,他的隐式原型指向传入的对象。
Son.prototype.constructor = Son
const son = new Son('菠萝蜜', 40)
console.log(son.prototype.name) //原型上已经没有name属性了,所以这里会报错
7,混入继承
利用object.assign的方法多个父类函数的原型拷贝给子类原型
function Father(name){
this.name = name
}
Father.prototype.showName = function(){
console.log(this.name)
}
function Mather(name){
this.name = name
}
Mather.prototype.showColor = function(){
console.log(this.color)
}
function Son(name, color, age){
// 调用两个父类函数
Father.call(this, name)
Mather.call(this, color)
this.age = age
}
Son.prototype = Object.create(Father.prototype)
Object.assign(Son.prototype, Mather.prototype)
const son = new Son('苹果','red', 45)
son.showColor() // red
8,class继承
class里的extends和super关键字,继承效果与寄生组合继承一样。
class Father{
constructor(name){
this.name = name
}
showName(){
console.log(this.name)
}
}
class Son extends Father{
constructor(name, age){
super(name)
this.age = age
}
showAge(){
console.log(this.age)
}
}
const son = new Son('风筝', 35)
son.showName() // 风筝
son.showAge() // 35