9021年底了,突然想在這個最後一個月準備一下,試試機會,能否更進一步。所以開始準備一些基礎知識,也隨帶總結出來給各位想換工作的同學。希望大家能找到自己想要的工作。祝大家好運!
一、何為繼承
一個類獲取另一個或者多個類的屬性或者方法。繼承可以使得子類具有父類的各種方法和屬性。以免重複輸出很多程式碼。
二、繼承的原理
複製父類的方法和屬性來重寫子類的原型物件。
三、原型鏈繼承
3.1 實現
function Father() {
this.text = '1';
}
Father.prototype.someFn = function() {
console.log(1);
}
Father.prototype.someValue = '2';
function Son(){
this.text1 = 'text1';
}
// 函式原型指向建構函式的例項
Son.prototype = new Father();
複製程式碼
3.2 優點
1、簡單易操作。
3.3 缺點
1、父類使用this宣告的屬性被所有例項共享。 原因是例項化是父類一次性賦值到子類例項的原型上,它會將父類通過this宣告的屬性也賦值到子類原型上。例如在父類中一個陣列值,在子類的多個例項中,無論哪一個例項去修改這個陣列的值,都會影響到其他子類例項。
2、建立子類例項時,無法向父類建構函式傳參,不夠靈活。
四、借用建構函式(call)
4.1 實現
function Father(...arr) {
this.some = '父類屬性';
this.params = arr;
}
Father.prototype.someFn = function() {
console.log(1);
}
Father.prototype.someValue = '2';
function Son(fatherParams, ...sonParams) {
// Father的this指向Son的this
// 使用call呼叫父類,Father將會立即被執行,並且將父類的Father的this執行Son
// 的this。例項化子類,this將指向new期間建立的新物件,返回該新物件。
Father.call(this, ...fatherParams);
this.text = '子類屬性';
this.sonParams = sonParams;
}
var fatherParams = [];
var sonParams = [];
var sonInstance = new Son(fatherParams, ...sonParams);
複製程式碼
4.2 優點
1、可以向父類傳遞引數。
2、解決父類this宣告的屬性會被例項共享的問題。
4.3 缺點
1、只能繼承父類通過this宣告的屬性/方法。不能繼承父類prototype上的屬性/方法。
2、父類方法無法複用。每次例項化子類,都要執行父類函式。重新宣告父類所定義的方法,無法複用。
五、組合繼承(call+new)
原理:通過原型鏈繼承來將this、prototype上的屬性和方法繼承製子類的原型物件上。使用借用建構函式來繼承父類通過this宣告的屬性和方法在之子類的例項屬性上。
5.1 實現
function Father(...arr) {
this.some = '父類屬性';
this.params = arr;
}
Father.prototype.someFn = function() {
console.log(1);
}
Father.prototype.someValue = '2';
function Son(fatherParams, ...sonParams) {
// 借用建構函式繼承父類this什麼的屬性和方法到子類例項屬性上
Father.call(this, ...fatherParams);
this.text = '子類屬性';
this.sonParams = sonParams;
}
// 原型鏈繼承,將`this`和`prototype`宣告的屬性/方法繼承至子類的`prototype`上
Son.prototype = new Father('xxxxx');
var fatherParams = [];
var sonParams = [];
var sonInstance = new Son(fatherParams, ...sonParams);
複製程式碼
5.2 優點
1、解決原型鏈繼承父類this宣告的屬性或者方法被共享的問題。
2、解決借用建構函式解決不能繼承父類prototype物件上的屬性/方法問題。
5.3 缺點
1、呼叫了父類函式兩次,造成一定的效能問題。
2、因呼叫兩次父類,匯出父類通過this宣告的屬性和方法被生成兩份的問題。
3、原型鏈上下文丟失,子類和父類通過prototype宣告的屬性和方法都存在與子類prototype上。
六、原型式繼承
6.1 實現
function cloneObj(obj) {
function F(){};
// 將被繼承的物件作為空函式的prototype
F.prototype = obj;
// 返回new期間建立的新物件,此物件的原型為被繼承的物件,
// 通過原型鏈查詢可以拿到被繼承物件的屬性
return new F();
}
複製程式碼
6.2 優點
1、相容性好,最簡單的物件繼承。
6.3 缺點
1、多少例項共享被繼承的屬性,存在被篡改的情況,不能傳遞引數。
七、寄生式繼承(繼承過程封裝)
建立一個僅用於封裝繼承過程的函式,改函式在內部已某種方式類增強物件,最後返回物件。在原型式繼承的基礎上進行增強物件。
7.1 實現
function createAnother(original){
var clone = cloneObject(original); // 繼承一個物件 返回新函式
// do something 以某種方式來增強物件
clone.some = function(){}; // 方法
clone.obkoro1 = '封裝繼承過程'; // 屬性
return clone; // 返回這個物件
}
複製程式碼
7.2 優點
1、相容性好,最簡單的物件繼承。
7.3 缺點
1、多少例項共享被繼承的屬性,存在被篡改的情況,不能傳遞引數。
八、寄生組合式繼承(call+寄生式封裝)
1、使用借用建構函式來繼承父類this宣告的屬性和方法。 2、使用寄生式繼承來設定父類prototype為子類prototype的原型來繼承父類的屬性和方法。
8.1 實現
function Father(...arr) {
this.some = '父類屬性';
this.params = arr;
}
Father.prototype.someFn = function() {
console.log(1);
}
Father.prototype.someValue = '2';
function Son() {
Father.call(this, 'xxxx');
this.text = '2222';
}
function inhertPro(son, father){
// 原型式繼承
var fatherPrototype = Object.create(father.prototype);
// 設定Son.prototype的原型是Father.prototype
son.prototype = fatherPrototype;
// 修正constructor 指向
// constructor的作用:返回建立例項物件的Object建構函式的引用。
// 在這裡保持constructor指向的一致性
son.prototype.constructor = son;
}
inhertPro(Son, Father);
var sonInstance = new Son();
複製程式碼
8.2 優點
1、寄生組合式繼承是當前最成熟的繼承方法,也是先也常用的繼承方法,在大多數Js框架中都是用這個作為繼承方案。
寄生組合式繼承相對組合繼承的優點:
1、只呼叫了父類建構函式一次,節約了效能。
2、避免生成了不必要的屬性。
3、使用原型式繼承保證了原型鏈上下文不變,子類的prototype只有子類通過prototype宣告的屬性和方法,父類的prototype只有父類通過prototype宣告的屬性和方法。
九、ES6-extends繼承
9.1 實現
ES6可以用過extends關鍵字實現繼承,這比通過ES5的修改原型鏈實現繼承,要清晰和方法很多。
class Point{}
class ColorPoint extends Point{}
複製程式碼
9.2 注意
子類必須在constructor方法中代用super方法,否則新建例項將會報錯,這是因為子類自己的this物件,必須先通過父類的建構函式完成塑性,得到父類的屬性和方法,然後對其加工,加上子類自己的屬性和方法。如果不呼叫super方法,子類將得不到this物件。如果沒有定義constructor方法,這個方法會被預設的新增。
9.3 轉換
ES6繼承的原理跟寄生組合式繼承是一樣的。優缺點也相仿。
把ES6的程式碼裝換為ES5 www.babeljs.cn/repl
轉換前:
class Point{}
class ColorPoint extends Point{}
複製程式碼
轉換後:
轉換的結果核心程式碼如下:用於子類的prototype繼承父類的prototype方法。function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass, writable: true, configurable: true
}
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
複製程式碼
9.4 區別
ES5的繼承實質是先建立子類的例項物件this,然後將父類的方法新增到this上。
ES6的繼承實質是先將父類例項物件的方法和屬性加到this上面,然後在用子類的建構函式修改this。
參考
- JS基礎-深入淺出繼承
- JavaScript高階程式設計