說(shuō)到繼承呢限嫌?肯定有很多做java的朋友都覺(jué)得是一個(gè)比較簡(jiǎn)單的東西了靴庆。畢竟面向?qū)ο蟮娜筇卣骶褪牵悍庋b、繼承和多態(tài)嘛怒医。但是真正對(duì)于一個(gè)javascript開(kāi)發(fā)人員來(lái)說(shuō)炉抒,很多時(shí)候其實(shí)你使用了繼承,但其實(shí)你不知道這叫繼承稚叹。今天我就借這篇文章來(lái)談一談繼承在前端的幾種實(shí)現(xiàn)方式焰薄。
一拿诸、 原型繼承
function Animal(name = 'animal'){
this.name = name
}
Animal.prototype.eat = function(food){
console.log('dasdsa')
return `${this.name} eat ${food}`;
}
function Dog(){
}
Dog.prototype = new Animal();
var instance = new Dog();
instance.name = 'dog';
console.log(instance.eat('bone'));
console.log(instance instanceof Dog); // true
console.log(instance instanceof Animal); // true
但是原型繼承有有有些缺點(diǎn),來(lái)看下面一段代碼:
function Animal(name = 'animal'){
this.name = name
this.skinColors = ['black','white']
}
Animal.prototype.eat = function(food){
return `${this.name} eat ${food}`;
}
function Dog(){
}
Dog.prototype = new Animal();
var instance = new Dog();
instance.name = 'keji';
instance.skinColors.push('red');
console.log(instance.eat('bone'));
console.log(instance instanceof Dog); // true
console.log(instance instanceof Animal); // true
var instance1 = new Dog()
console.log(instance1.skinColors) // [ 'black', 'white', 'red' ]
從上面的代碼塞茅,我們可以清楚的發(fā)現(xiàn):所有的實(shí)例都會(huì)公用一個(gè)原型鏈亩码,如果一個(gè)實(shí)例中修改原型 那么所有實(shí)例的值都會(huì)被修改。
二野瘦、 構(gòu)造函數(shù)繼承
針對(duì)前面原型鏈繼承可能會(huì)存在公用一個(gè)原型鏈的問(wèn)題描沟,那么我們可以給大家介紹一種方式:構(gòu)造函數(shù)的繼承。構(gòu)造函數(shù)的繼承相當(dāng)于將父類復(fù)制給子類鞭光。
function Animal(name = 'animal'){
this.name = name
this.skinColors = ['black','white']
}
Animal.prototype.eat = function(food){
return `${this.name} eat ${food}`;
}
function Dog(){
Animal.call(this);
}
var instance = new Dog();
instance.name = 'keji';
instance.skinColors.push('red');
console.log(instance.eat('bone')); // TypeError: instance.eat is not a function
console.log(instance instanceof Dog); // true
console.log(instance instanceof Animal); // true
var instance1 = new Dog();
console.log(instance1.skinColors); // [ 'black', 'white' ]
但是這種方法也有自己缺點(diǎn):
- 不能繼承原型上面的屬性和方法
- 復(fù)制的處理吏廉,相當(dāng)于在子類中實(shí)現(xiàn)了所有父類的方法,影響子類的性能衰猛。
三迟蜜、 組合繼承
原型鏈繼承能繼承父類原型鏈上的屬性,但是可能會(huì)存在篡改的問(wèn)題啡省;而構(gòu)造函數(shù)繼承不會(huì)存在篡改的問(wèn)題娜睛,但是不能繼承原型上面的屬性。那么我們是不是可以將兩者進(jìn)行結(jié)合呢卦睹?
function Animal(name = 'animal'){
this.name = name
this.skinColors = ['black','white']
}
Animal.prototype.eat = function(food){
return `${this.name} eat ${food}`;
}
function Dog(){
Animal.call(this);
}
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;
var instance = new Dog();
instance.name = 'keji';
instance.skinColors.push('red');
console.log(instance.eat('bone'));
console.log(instance.skinColors) // [ 'black', 'white', 'red' ]
console.log(instance instanceof Dog); // true
console.log(instance instanceof Animal); // true
var instance1 = new Dog()
console.log(instance1.skinColors) // [ 'black', 'white' ]
這種方法呢畦戒?調(diào)用了兩次父類的構(gòu)造函數(shù),有些許損耗性能结序,并且子類的構(gòu)造函數(shù)的屬性會(huì)和原型上面的屬性相重合障斋。(優(yōu)先原用構(gòu)造函數(shù)的屬性)
四、 原型式繼承
function object(obj){
function F(){}
F.prototype = obj;
return new F();
}
let Programmer = {
features:["tutou","jiaban","single"]
}
// 方式一:最原始的做法
var programmer1 = object(Programmer);
programmer1.features.push('meiqian');
console.log(programmer1.features); // [ 'tutou', 'jiaban', 'single', 'meiqian' ]
var programmer2 = object(Programmer);
console.log(programmer2.features); // [ 'tutou', 'jiaban', 'single', 'meiqian' ]
// 方式二 es中的Object.create
var programmer3 = Object.create(Programmer);
console.log(programmer3.features); // [ 'tutou', 'jiaban', 'single', 'meiqian' ]
從上面的代碼很明顯的可以發(fā)現(xiàn):和構(gòu)造函數(shù)繼承一樣也存在被篡改的可能徐鹤,并且也不能傳遞參數(shù)垃环。
五、 寄生式繼承
在原型式繼承的基礎(chǔ)上面增強(qiáng)了對(duì)象返敬,并返回構(gòu)造函數(shù)遂庄。
function pFactory(obj){
let clone = Object.create(obj);
clone.motto = function(){
console.log('hardworking and not lazy!!')
}
return clone;
}
var programmer1 = new pFactory(Programmer);
console.log(programmer1.motto()); // hardworking and not lazy!!
console.log(programmer1.features); // [ 'tutou', 'jiaban', 'single' ]
這種繼承的方法同樣和原型繼承一樣,存在被篡改的可能劲赠。
六涛目、 寄生組合式繼承
前面說(shuō)了這么多,每種繼承方式都有自己的優(yōu)點(diǎn)和缺點(diǎn)凛澎,那么是不是可以將這些繼承的方式做一個(gè)合并:以他之長(zhǎng)補(bǔ)己之短呢霹肝?來(lái)看下面一段代碼:
function Animal(name = 'animal'){
this.name = name
this.skinColors = ['black','white']
}
Animal.prototype.eat = function(food){
return `${this.name} eat ${food}`;
}
function inheritPrototype(subType, superType){
var prototype = Object.create(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
function Dog(name,sound){
Animal.call(this,name);
this.sound = sound;
}
inheritPrototype(Dog,Animal);
Dog.prototype.getSound = function(){
console.log(`${this.name} ${this.sound}`);
}
var instance = new Dog('keji','wangwangwang!!!');
instance.skinColors.push('red');
console.log(instance.eat('bone'));
console.log(instance.skinColors) // [ 'black', 'white', 'red' ]
console.log(instance instanceof Dog); // true
console.log(instance instanceof Animal); // true
console.log(instance.getSound()) // keji wangwangwang!!!
var instance1 = new Dog('haha','wangwang!!!')
console.log(instance1.skinColors) // [ 'black', 'white' ]
console.log(instance1.getSound()) // haha wangwang!!!
這個(gè)例子的效率的體現(xiàn)在它只調(diào)用了一次父類的構(gòu)造函數(shù),這很大程度上面減少創(chuàng)建了不必要多余的屬性塑煎。并且還能繼承原型鏈上面的方法沫换。這個(gè)方法是現(xiàn)在庫(kù)的實(shí)現(xiàn)方法。
七轧叽、 es6的繼承方法
class Animal {
constructor(name){
this.name = name;
}
get getName(){
return this.animalName()
}
animalName(){
return this.name;
}
}
class Dog extends Animal{
constructor(name,sound){
super(name);
this.sound = sound;
}
get animalFeature(){
return `${this.getName} ${this.sound}`
}
}
let dog = new Dog('keji','wangwangwang!');
console.log(dog.animalFeature); // keji wangwangwang!
其實(shí)我們曉得苗沧,class語(yǔ)法也是由es5語(yǔ)法來(lái)寫(xiě)的刊棕,其繼承的方法和寄生組合式繼承的方法一樣。關(guān)于es6的類待逞,我在代碼自檢的時(shí)候遇到的兩個(gè)重點(diǎn)甥角,值得注意下的是:
- 函數(shù)聲明會(huì)提升,類聲明不會(huì)识樱。
- ES5的繼承實(shí)質(zhì)上是先創(chuàng)建子類的實(shí)例對(duì)象嗤无,然后再將父類的方法添加到this上。但是es6是先創(chuàng)建父類的實(shí)例對(duì)象this怜庸,然后再用子類的構(gòu)造函數(shù)修改this当犯。
說(shuō)在最后
好像什么都沒(méi)寫(xiě)就差不多快12點(diǎn)了,最近在瘋狂的復(fù)習(xí)割疾。但是卻發(fā)現(xiàn)越學(xué)東西越多嚎卫,感覺(jué)有點(diǎn)學(xué)不完的意味在里面 外加上最近好像有點(diǎn)高考考砸之后的失眠綜合癥,搞的我整個(gè)人都不怎么舒服宏榕。明明高考都過(guò)去差不多6年了拓诸,還一直困擾著我,賊恐怖麻昼,算了 算了 先不寫(xiě)了 睡覺(jué)去了奠支。