經過上一篇文章想必各位老鐵已經熟悉了class
了,這篇文章接着介紹繼承。面向對象裏最大的特色應該就屬繼承了。一個項目可能須要不斷的迭代、完善、升級。那每一次的更新你是要從新寫呢,仍是在原有的基礎上改吧改吧呢?固然,不是缺心眼的人確定都會在原來的基礎上改吧改吧,那這個改吧改吧就須要用到繼承了。javascript
在第二篇文章裏說過原型實例跟構造函數之間的繼承,而且還講了一道推算題。最終咱們明白,實例爲何能繼承原型上的內容是由於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
修改爲子類,就這麼神奇!java
new
是生成實例的命令。ES6
爲new
命令引入了一個new.target
屬性,該屬性通常用在構造函數之中segmentfault
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的對好比下:this
//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
新增的方法,做用是把第一個參數的原型設置成第二個參數),如下爲內部過程:prototype
{ 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
方法的內部是這樣的:code
//setPrototypeOf方法內部主要代碼 Object.setPrototypeOf=function(obj,proto){ obj.__proto__=proto; return obj; }
下一篇文章介紹super關鍵字對象