通過上一篇文章想必各位老鐵已經(jīng)熟悉了class
了屯远,這篇文章接著介紹繼承。面向?qū)ο罄镒畲蟮奶攸c應(yīng)該就屬繼承了惕耕。一個項目可能需要不斷的迭代、完善诫肠、升級赡突。那每一次的更新你是要重新寫呢,還是在原有的基礎(chǔ)上改吧改吧呢区赵?當(dāng)然惭缰,不是缺心眼的人肯定都會在原來的基礎(chǔ)上改吧改吧,那這個改吧改吧就需要用到繼承了笼才。
在第二篇文章里說過原型實例跟構(gòu)造函數(shù)之間的繼承漱受,并且還講了一道推算題。最終我們明白骡送,實例為什么能繼承原型上的內(nèi)容是因為prototype
昂羡,所以在ES5
里面想要繼承的話就得通過原型,需要對prototype
進(jìn)行一頓蹂躪才行摔踱。那到了ES6
里面一切就簡單了虐先,像開了掛似的!so easy派敷,哪里不會點哪里蛹批!
繼承
- class類可以通過extends實現(xiàn)繼承
- 利用super關(guān)鍵字引入父類的構(gòu)造函數(shù)
- ES6規(guī)定子類必需在構(gòu)造函數(shù)(constructor)里先調(diào)用super方法
- 子類能同時繼承父類的共享方法與私有方法
//這個類做為父類('老王')
class OldWang{
constructor(work,money){
this.work=work;
this.money=money;
}
showWork(){
console.log(`老王是個${this.work},看了我的文章后篮愉,能力達(dá)到了${this.level}腐芍,一個月能掙${this.money}元`);
}
static play(){ //這是個私有方法,但子類依然能繼承到
console.log('大吉大利试躏,今晚吃雞猪勇!不會玩游戲的前端不是個好前端!');
}
}
//子類繼承父類
class SmallWang extends OldWang{
constructor(work,money,level){
//這里必需先寫super颠蕴,不然會報錯
super(work,money,level);
this.level=level; //只有用了super泣刹,才能使用this
}
}
//生成實例
const wang=new SmallWang('前端',20000,'T5');
wang.showWork(); //老王是個前端助析,看了我的文章后,能力達(dá)到了T5椅您,一個月能掙20000元
SmallWang.play(); //大吉大利外冀,今晚吃雞!不會玩游戲的前端不是個好前端襟沮! 子類能繼承父類的私有方法
//與ES5里的實例是一致的
console.log(
Object.getPrototypeOf(SmallWang)===OldWang, //true 子類的原型是OldWang锥惋,也就是說,它是OldWang的實例
wang instanceof OldWang, //true
wang instanceof SmallWang, //true
);
ES5
的繼承开伏,實質(zhì)是先聲明子類膀跌,然后通過call
方法將父類的方法添加到子類上,而ES6
的繼承機制完全不同固灵。實質(zhì)是聲明了子類后捅伤,子類并沒有this
對象,而是利用super
方法引入父類的this
對象巫玻,再將this
修改成子類丛忆,就這么神奇!
new.target
new
是生成實例的命令仍秤。ES6
為new
命令引入了一個new.target
屬性熄诡,該屬性一般用在構(gòu)造函數(shù)之中
new.target
返回new
命令作用于的那個類- 子類繼承父類時,
new.target
返回子類
class Person{
constructor(){
//如果類不是通過new調(diào)用的诗力,就會返回undefined
if(new.target===undefined){
throw new Error('請使用new生成實例凰浮!');
}
console.log(new.target.name);
}
}
new Person(); //Person類(返回了new作用于的那個類)
Person(); //有些瀏覽器可以不帶new生成實例,就會拋出一個錯誤
class Man extends Person{
}
new Man(); //Man(子類繼承父類時苇本,new.target會返回子類)
//利用這個特性實現(xiàn)一個不能獨立使用袜茧,必需繼承后才能用的類(像React里的組件)
class Uncle{
constructor(){
if(new.target===Uncle){
throw new Error('這個類不能實例化,只能繼承后再用');
}
}
showUncle(){
console.log('都是他舅');
}
}
//new Uncle(); 報錯
//通過繼承就可以使用Uncle了
class BigUncle extends Uncle{
constructor(){
super(); //引入父類的構(gòu)造函數(shù)瓣窄,必須加不然報錯
this.uncle='他大舅';
}
}
//實例
const uncle=new BigUncle();
uncle.showUncle(); //都是他舅
原型
class
里的原型關(guān)系相對于ES5
里的原型關(guān)系笛厦,ES6
對其進(jìn)行了修改,但只修改了子類與父類之間的關(guān)系俺夕,其它的關(guān)系并沒有修改裳凸。
- 子類的
__proto__
,表示構(gòu)造函數(shù)的繼承啥么,指向父類構(gòu)造函數(shù)- 子類prototype屬性的
__proto__
登舞,表示方法的繼承,指向父類的prototype
ES5里的繼承關(guān)系悬荣,在第二篇文章里詳細(xì)介紹過,再回顧一下:
//ES5的繼承關(guān)系
const str=new String(123);
console.log(
str.__proto__===String.prototype疙剑, //true
String.__proto__===Function.prototype //true
);
//可以看到不管實例還是構(gòu)造函數(shù)氯迂,它們的__proto__屬性永遠(yuǎn)都指向原型
ES6與ES5的對比如下:
//ES5
function Ball(){}
function Football(){
Ball.call(this); //ES5的繼承
}
//ES6
class Father{};
class Son extends Father{}
//構(gòu)造函數(shù)践叠,關(guān)系沒變
console.log(
'構(gòu)造函數(shù)',
Ball.__proto__===Ball.prototype, //false
Father.__proto__===Father.prototype,//false
Ball.__proto__===Function.prototype, //true
Father.__proto__===Function.prototype //true
);
//實例,關(guān)系沒變
console.log(
'實例',
new Ball().__proto__===Ball.prototype, //true
new Father().__proto__===Father.prototype //true
);
//子類嚼蚀,關(guān)系變了
console.log(
'子類的__proto__',
Football.__proto__===Ball, //false ES5
Football.__proto__===Function.prototype,//true ES5
Son.__proto__===Father, //true ES6
Son.__proto__===Father.prototype, //false ES6
//ES6的變化為:子類的__proto__指向父類
);
console.log(
'子類的prototype的__proto__屬性',
Football.prototype.__proto__===Ball.prototype, //false ES5
Football.prototype.__proto__===Object.prototype,//true ESS
Son.prototype.__proto__===Object.prototype, //false ES6
Son.prototype.__proto__===Father.prototype, //true ES6
//ES6的變化為:子類的prototype的__proto__屬性指向父類的prototype
);
由此可以看出ES6
只修改了子類跟父類間的原型關(guān)系禁灼,其它的不受影響。那至于ES6
對這兩條關(guān)系做了修改的原因跟ES6
的繼承機制有關(guān)系轿曙,ES6
內(nèi)部的繼承用的是Object.setPrototypeOf
方法(ES6
新增的方法弄捕,作用是把第一個參數(shù)的原型設(shè)置成第二個參數(shù)),以下為內(nèi)部過程:
{
class Father{};
class Son{};
//son的實例繼承Father的實例导帝,內(nèi)部會執(zhí)行下面的代碼
Object.setPrototypeOf(Son.prototype,Father.prototype);
//等同于Son.prototype.__proto__=Father.prototype;所以得出結(jié)果:子類prototype屬性的__proto__屬性守谓,表示方法的繼承,指向父類的prototype屬性
//son繼承Father的私有屬性您单,內(nèi)部會執(zhí)行下面的代碼
Object.setPrototypeOf(Son,Father);
//等同于Son.__proto__=Father;所以得出結(jié)果:子類的__proto__屬性斋荞,表示構(gòu)造函數(shù)的繼承,指向父類
}
為什么用了setPrototypeOf
后虐秦,等價于把第一個參數(shù)的__proto__
的值設(shè)置成第二個參數(shù)平酿?是因為setPrototypeOf
方法的內(nèi)部是這樣的:
//setPrototypeOf方法內(nèi)部主要代碼
Object.setPrototypeOf=function(obj,proto){
obj.__proto__=proto;
return obj;
}
下一篇文章介紹super關(guān)鍵字