在 ES6 之前,JavaScript 實現(xiàn)兩個對象的繼承一般有兩種方法。
一種方法是利用 this 與構(gòu)造函數(shù)。
function Parent(name,height) {
this.name = name;
this.height = height;
}
function Child(age) {
Parent.apply(this,['hahaha',171])
this.age = age;
}
let ch = new Child(21);
console.log(ch.name)
在這種方法中,利用 apply 或 call,在一個函數(shù)中改變了另一個函數(shù)的 this 指向并運(yùn)行。結(jié)果是一個構(gòu)造函數(shù)的實例,繼承了另一個構(gòu)造函數(shù)的屬性和方法。這種方法有一個缺點,對于原型鏈上的繼承沒有實現(xiàn)。
另一種方法是利用原型鏈實現(xiàn)屬性的繼承。
function Parent(name,height) {
this.name = name;
this.height = height;
}
function Child(age) {
this.age = age;
}
Child.prototype = new Parent('hahaha',171);
let ch = new Child(21)
console.log(ch.name)
這種方法與其說是繼承,不如說是一種查找規(guī)則。JavaScript 中如果在一個對象中找不到相關(guān)屬性和方法,就會沿著原型鏈向上查找。在這段代碼中,將 Child 的原型對象設(shè)置為 Parent 的一個實例,也就是將 Child 實例原型鏈的上一層設(shè)置為 Parent 的一個實例。由此實現(xiàn)了一個對象繼承了另一個對象的效果。
這種方法也有個缺點,繼承了同一個原型的兩個實例之間相互影響。
在 ES6 之后,有了類的書寫方法。
class Fruit {
constructor (name,quantity){
this.name = name;
this.quantity = quantity;
}
static show() {
return 'show'
}
getName() {
return this.name;
}
}
class Apple extends Fruit {
constructor (name,quantity,area) {
super(name,quantity);
this.area = area;
}
}
let apple = new Apple('apple',3,'shan');
console.log(Apple.show() +':'+ apple.getName()+' ' + apple.quantity)
// show:apple 3
類的繼承語法更直觀,書寫更簡便,它與 ES5 的繼承有幾點不同。
getPrototypeOf 結(jié)果不同
類繼承中子類會 [[Prototype]] 鏈接到父類,原型繼承中的構(gòu)造函數(shù)都是通過 prototype 指向的原型對象相互聯(lián)系的。一般在原型繼承中,子構(gòu)造函數(shù)的原型對象是父構(gòu)造函數(shù),或者子構(gòu)造函數(shù)的原型對象 [[Prototype]] 鏈接到父構(gòu)造函數(shù)的原型對象。this 創(chuàng)造順序不同
ES5 的繼承,實質(zhì)是先創(chuàng)造子類的實例對象this,然后再將父類的方法添加到this上面(Parent.apply(this))。ES6 的繼承先將父類實例對象的屬性,加到this上面(所以必須先調(diào)用super方法),然后再在子類中修改this。子類實例的構(gòu)建,基于父類實例,只有super方法才能調(diào)用父類實例。
參考文章阮一峰: Class 的繼承
