摘要:面向對象里最大的特點應該就屬繼承了。在第二篇文章里說過原型實例跟構造函數之間的繼承,并且還講了一道推算題。
通過上一篇文章想必各位老鐵已經熟悉了class了,這篇文章接著介紹繼承。面向對象里最大的特點應該就屬繼承了。一個項目可能需要不斷的迭代、完善、升級。那每一次的更新你是要重新寫呢,還是在原有的基礎上改吧改吧呢?當然,不是缺心眼的人肯定都會在原來的基礎上改吧改吧,那這個改吧改吧就需要用到繼承了。
在第二篇文章里說過原型實例跟構造函數之間的繼承,并且還講了一道推算題。最終我們明白,實例為什么能繼承原型上的內容是因為prototype,所以在ES5里面想要繼承的話就得通過原型,需要對prototype進行一頓蹂躪才行。那到了ES6里面一切就簡單了,像開了掛似的!so easy,哪里不會點哪里!
繼承class類可以通過extends實現繼承
利用super關鍵字引入父類的構造函數
ES6規定子類必需在構造函數(constructor)里先調用super方法
子類能同時繼承父類的共享方法與私有方法
//這個類做為父類("老王") class OldWang{ constructor(work,money){ this.work=work; this.money=money; } showWork(){ console.log(`老王是個${this.work},看了我的文章后,能力達到了${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(); //老王是個前端,看了我的文章后,能力達到了T5,一個月能掙20000元 SmallWang.play(); //大吉大利,今晚吃雞!不會玩游戲的前端不是個好前端! 子類能繼承父類的私有方法 //與ES5里的實例是一致的 console.log( Object.getPrototypeOf(SmallWang)===OldWang, //true 子類的原型是OldWang,也就是說,它是OldWang的實例 wang instanceof OldWang, //true wang instanceof SmallWang, //true );
ES5的繼承,實質是先聲明子類,然后通過call方法將父類的方法添加到子類上,而ES6的繼承機制完全不同。實質是聲明了子類后,子類并沒有this對象,而是利用super方法引入父類的this對象,再將this修改成子類,就這么神奇!
new.targetnew是生成實例的命令。ES6為new命令引入了一個new.target屬性,該屬性一般用在構造函數之中
new.target返回new命令作用于的那個類
子類繼承父類時,new.target返回子類
class Person{ constructor(){ //如果類不是通過new調用的,就會返回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會返回子類) //利用這個特性實現一個不能獨立使用,必需繼承后才能用的類(像React里的組件) class Uncle{ constructor(){ if(new.target===Uncle){ throw new Error("這個類不能實例化,只能繼承后再用"); } } showUncle(){ console.log("都是他舅"); } } //new Uncle(); 報錯 //通過繼承就可以使用Uncle了 class BigUncle extends Uncle{ constructor(){ super(); //引入父類的構造函數,必須加不然報錯 this.uncle="他大舅"; } } //實例 const uncle=new BigUncle(); uncle.showUncle(); //都是他舅原型
class里的原型關系相對于ES5里的原型關系,ES6對其進行了修改,但只修改了子類與父類之間的關系,其它的關系并沒有修改。
子類的__proto__,表示構造函數的繼承,指向父類構造函數
子類prototype屬性的__proto__,表示方法的繼承,指向父類的prototype
ES5里的繼承關系,在第二篇文章里詳細介紹過,再回顧一下:
//ES5的繼承關系 const str=new String(123); console.log( str.__proto__===String.prototype, //true String.__proto__===Function.prototype //true ); //可以看到不管實例還是構造函數,它們的__proto__屬性永遠都指向原型
ES6與ES5的對比如下:
//ES5 function Ball(){} function Football(){ Ball.call(this); //ES5的繼承 } //ES6 class Father{}; class Son extends Father{} //構造函數,關系沒變 console.log( "構造函數", Ball.__proto__===Ball.prototype, //false Father.__proto__===Father.prototype,//false Ball.__proto__===Function.prototype, //true Father.__proto__===Function.prototype //true ); //實例,關系沒變 console.log( "實例", new Ball().__proto__===Ball.prototype, //true new Father().__proto__===Father.prototype //true ); //子類,關系變了 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只修改了子類跟父類間的原型關系,其它的不受影響。那至于ES6對這兩條關系做了修改的原因跟ES6的繼承機制有關系,ES6內部的繼承用的是Object.setPrototypeOf方法(ES6新增的方法,作用是把第一個參數的原型設置成第二個參數),以下為內部過程:
{ class Father{}; class Son{}; //son的實例繼承Father的實例,內部會執行下面的代碼 Object.setPrototypeOf(Son.prototype,Father.prototype); //等同于Son.prototype.__proto__=Father.prototype;所以得出結果:子類prototype屬性的__proto__屬性,表示方法的繼承,指向父類的prototype屬性 //son繼承Father的私有屬性,內部會執行下面的代碼 Object.setPrototypeOf(Son,Father); //等同于Son.__proto__=Father;所以得出結果:子類的__proto__屬性,表示構造函數的繼承,指向父類 }
為什么用了setPrototypeOf后,等價于把第一個參數的__proto__的值設置成第二個參數?是因為setPrototypeOf方法的內部是這樣的:
//setPrototypeOf方法內部主要代碼 Object.setPrototypeOf=function(obj,proto){ obj.__proto__=proto; return obj; }
下一篇文章介紹super關鍵字
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/96656.html
摘要:源碼下載至此再和面向對象談戀愛系列文章已經全部更新完畢寫文章不易,且行且珍惜 再和面向對象談戀愛 - 對象簡介(一)再和面向對象談戀愛 - 對象相關概念(二)再和面向對象談戀愛 - 面向對象編程概念(三)再和面向對象談戀愛 - class(四)再和面向對象談戀愛 - 繼承(五)再和面向對象談戀愛 - super(六) 通過前面的六篇文章已經把ES6的面向對象跟大伙說清楚了,大家最關心的...
摘要:在上一篇文章里我介紹了一下面向對象編程的概念,在最后終于喜出望外看到了提供了類的概念了。而到了里面真正的類與構造函數現在是分離的,通過上面的代碼可以看出來,這種寫法正是面向對象的正統寫法。 在上一篇文章里我介紹了一下面向對象編程的概念,在最后終于喜出望外看到了ES6提供了類的概念了。那這個類如何去用,是這篇文章的主題。ES6給我們提供了一個class關鍵字。這個關鍵字跟以前的var l...
摘要:所有的對象都是由構造函數創建的對象哪來的構造函數生的。而普通函數不能生成對象不孕不育,構造函數可以生成對象有生育能力。別急,記住那句話永遠指向實例對象對應的構造函數的,那就先看實例對象是誰。 上一篇文章把對象的概念講解了一下,這篇文章要重點解釋最讓大家犯迷糊的一些概念,包括 構造函數 實例 繼承 構造函數的屬性與方法(私有屬性與方法) 實例的屬性與方法(共享屬性與方法) protot...
摘要:同時彈出的結果是指向了子類,又說明雖然調用的是父類的構造函數,但是調用完后會指向子類,指向也被改成了子類的實例。 在上一篇文章里介紹了繼承,那其中說過一個很關鍵的東西想要繼承子類里里必需先調用一個super方法。而super的作用絕對是價值連城!同時super的作用還有多種,并且跟你的使用環境有關系。 1、當作函數使用 super被當作函數使用,這種情況是最普遍的,上一篇文章里已經使用...
摘要:說到底面向對象才是程序語言的根本。其實面向對象編程說的就是自定義對象。里并沒有類的概念,所以嚴格上來講這是個假的面向對象里的面向對象編程現在好了,終于聽到別人鄙視我們了,給我們提供了類這個概念,其實是向傳統語言更靠齊了。 通過前兩篇文章,我們了解了對象的概念以及面向對象里的相關概念等知識,那前面說了對象分類里的前兩種,這篇文章要詳細去說第三種自定義對象,那真正的好戲這就來了! 面向對象...
閱讀 3766·2021-11-11 11:02
閱讀 3495·2021-10-11 10:57
閱讀 3608·2021-09-22 16:00
閱讀 1843·2021-09-02 15:15
閱讀 1322·2019-08-30 15:56
閱讀 1005·2019-08-30 15:54
閱讀 2731·2019-08-30 12:43
閱讀 3539·2019-08-29 16:06