這樣回答繼承,面試官可能更滿意

蝸牛的北極星之旅發表於2019-12-04

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。

參考

相關文章