ES6繼承和ES5繼承是完全一樣的么?
- 2021 年 8 月 17 日
- 筆記
- ES6系統學習, javascript, 本周面試題, 面試題
繼承方式
ES5 prototype 繼承
通過原型鏈(構造函數 + [[prototype]]
)指向實現繼承。 (備註:後續__proto__我都會寫成[[prototype]]這種形式)
子類的 prototype 為父類對象的一個實例。因此子類的原型對象包含指向父類的原型對象的指針,父類的實例屬性為子類原型的屬性。
// 父類:function SuperType;子類:function SubType;
SubType.prototype = new SuperType(); // SubType 繼承 SuperType
// 根據上一節原型鏈講到的知識點:實例化對象的__proto__等於構造函數的prototype
SubType.prototype.__proto__ === SuperType.prototype // true
上邊的繼承關係如下圖:
內部實現機制上,ES5 的繼承,實質是先創造子類的實例對象this上,然後再將父類的方法添加到這個this上。類似使用:Father.apply(this)
ES6 class 繼承
通過class的extends
+ super
實現繼承。
子類沒有自己的this對象,因此必須在 constructor 中通過 super 繼承父類的 this 對象,而後對此this對象進行添加方法和屬性。
super關鍵字在構造函數中表示父類的構造函數,用來新建父類的 this 對象。
內部實現機制上,ES6 的繼承機制完全不同,實質是先創造父類的實例對象this—需要提前調用super方法,然後再用子類的構造函數修改this指針。
super用法
super
可以作為函數和對象使用的。
當作為函數使用的時候,只能在子類的構造函數中使用—-表示父類的構造函數,但是 super 中的 this 指向的是子類的實例,因此在子類中super()表示的是 Father.prototype.constructor.call(this)。
當作為對象使用的時候,super表示父類的原型對象,即表示 Father.prototype
二者區別
答:不是完全一樣的,主要有以下幾個差異點:
-
寫法不一樣。class的繼承通過extends關鍵字和super函數、super方法繼承。(關於super實現繼承的使用方式,具體我就不展開了) -
類內部定義的方法都是不可枚舉的,這個 ES5 不一樣 -
類不存在變數提升,這一點與 ES5 完全不同 -
類相當於實例的原型,所有在類中定義的方法都會被實例繼承。如果在一個方法前,加上 static 關鍵字,就表示該方法不會被實例繼承,而是直接通過類來調用,這就成為靜態方法 -
內部實現機制不一樣。
ES5 prototype 繼承 內部實現方式
ES5的繼承,實質上是先創造子類的實例對象this,然後再將父類的方法添加到子類(this)上面—Father.apply(this)。

ES6 class 繼承 內部實現方式
ES6的繼承機制完全不同,實質上是先創造父類的實例對象this,並將父類的屬性和方法放到this上(前提是通過super函數調用),然後再用子類的構造函數修改this。

因為實現機制不同,導致這兩種繼承在繼承原生構造函數時有些差異:
es5的寫法不能繼承原生構造函數(比如Array、Number等)
因為es5的繼承是先創造子類的實例對象this,再將父類原型的屬性和方法重寫到子類上,因為沒法訪問父類的內部屬性,導致es5的繼承方式無法繼原生的構造函數。
es6允許繼承構造函數生成子類。因為es6是先創建父類的實例對象this,然後再用子類的構造函數修飾,所以子類就可以繼承父類的所有屬性和方法。因此class可以繼承並自定義原生構造函數的子類。extends不僅可以用來繼承類,還能用來繼承原生構造函數,因此也就可以在原生數據結構的基礎上,構造自定義的數據結構。
擴展
關於內部實現機制的說明,可以參考《阮一峰的es6文檔-class的繼承》相關部分
文中有不妥之處歡迎留言討論,更多【每日一題】盡在公眾號 【前端印記】。