粗談繼承

Gu_Yan發表於2019-04-03

注:在繼承之前我們需要了解一下建構函式和原型鏈的相關知識,下面我通過例子來說明一下

/*
 * 所謂的建構函式,顧明思議就是函式,但其與普通函式也有不同 
 *  1.建構函式是通過 new 關鍵字建立物件時呼叫的函式
 *  2.通常建構函式我們會將函式名字的首字母大寫
 */
  function Animal(name){
    this.name = name; // 例項上的屬性
    this.ability = ['吃飯', '睡覺', '打豆豆']; // 例項上的屬性
    this.eat = function(){ // 例項上的方法
        console.log('吃')
    }
  }
  Animal.prototype.address = {location: '大山'}; // 原型上的屬性(公有屬性)
  let animal1 = new Animal('老虎');
  console.log(animal1) // Animal { name: '老虎', ability: [ '吃飯', '睡覺', '打豆豆' ], eat: [Function] }
  let animal2 = new Animal('貓'); 
  console.log(animal2) // Animal { name: '貓', ability: [ '吃飯', '睡覺', '打豆豆' ], eat: [Function] }
  console.log(animal1.ability === animal2.ability); // false
  console.log(animal1.address === animal2.address); // true
  console.log(animal1.__proto__ === Animal.prototype); // true
  console.log(Animal.prototype.constructor === Animal); // true
  console.log(Animal.__proto__ === Function.prototype); // true
  console.log(Function.prototype.__proto__ === Object.prototype); // true
  console.log(Object.prototype.__proto__); // null
  /*
   * 一句話總結原型鏈:每一個例項都有一個__proto__屬性,指向該例項所屬類的原型(prototype)【需要注意的是原型是Object的例項哦】;每一個類的原型上都有一個constructor屬性指向類的本身
   */
複製程式碼

繼承的定義

  • 【官方】繼承是物件導向軟體技術當中的一個概念。如果一個類別A“繼承自”另一個類別B,就把這個A稱為“B的子類別”,而把B稱為“A的父類別”也可以稱“B是A的超類”。繼承可以使得子類別具有父類別的各種屬性和方法,而不需要再次編寫相同的程式碼。在令子類別繼承父類別的同時,可以重新定義某些屬性,並重寫某些方法,即覆蓋父類別的原有屬性和方法,使其獲得與父類別不同的功能。另外,為子類別追加新的屬性和方法也是常見的做法。 一般靜態的物件導向程式語言,繼承屬於靜態的,意即在子類別的行為在編譯期就已經決定,無法在執行期擴充。(注:文字內容來源某度)
  • 【我】我理解的繼承無非就是兩個互不相關的類通過一些特定的方法使其中一個類擁有另一個類的屬性或者方法,JS中我總結了為有三種繼承場景
    • 1、子類繼承父類例項上的屬性/方法
    • 2、子類繼承父類原型上的屬性/方法
    • 3、子類繼承父類例項和原型上的屬性/方法

一、子類繼承父類例項上的屬性/方法(所謂的call繼承)

function Animal(name){ // 父類
    this.name = name;
    this.eat = '吃肉'
}
Animal.prototype.address = {location: '大山'};

function Tiger(name){
    Animal.call(this);
    this.name = name;
    this.age = 5;
}
Tiger.prototype.say = function(){ // 子類
    console.log('說話')
}
let tiger = new Tiger('老虎')
console.log(tiger) // Tiger { name: '老虎', eat: '吃肉', age: 10 }
console.log(tiger.say) // [Function]
console.log(tiger.address) // undefined
複製程式碼

二、子類繼承父類原型上的屬性/方法

實現方式一(所謂的原型鏈繼承)

function Animal(name){ // 父類
    this.name = name;
    this.eat = '吃肉';
}
Animal.prototype.address = {location: '大山'};

function Tiger(name){ // 子類
    this.name = name;
    this.age = 5;
}
Tiger.prototype.say = function(){
    console.log('說話');
}
Tiger.prototype.__proto__ = Animal.prototype;
let tiger = new Tiger('老虎');
console.log(tiger); // Tiger {name: '老虎', age: 10}
console.log(tiger.say); // [Function]
console.log(tiger.address) // {location: '大山'}
複製程式碼

實現方式二(通過Object.create/Object.setPrototypeOf)

function Animal(name){
    this.name = name;
    this.eat =  '吃肉';
}
Animal.prototype.address = {location: '大山'};

function Tiger(name){
    this.name = name;
    this.age = 5;
}
Tiger.prototype = Object.create(Animal.prototype);
// Tiger.prototype = Object.create(Animal.prototype,{constructor:{value:Tiger}});
// 上行程式碼等價於Object.setPrototypeOf(Tiger.prototype,Animal.prototype) ES6
Tiger.prototype.say = function(){
    console.log('說話')
}
let tiger = new Tiger('老虎')
console.log(tiger) // Animal { name: '老虎', age: 10 }
/* 這時候你會發現我們明明建立的是Tiger的例項,但是我們輸出的缺是Animal的例項,這就關係到Object.create方法的實現了,
 * 下面程式碼我們手寫一版簡單的Object.create方法(結合下文參考圖)
 */
Object.create = function(parentPrototype,options={}){
    let Fn = function(){};
    Fn.prototype = parentPrototype;
    let fn = new Fn();
    if(options.constructor){
        fn.constructor = options.constructor.value
    }
    return fn;
}
/*不難看出我們可以給Object.create傳入options來改變constructor*/
複製程式碼

參考圖

三、子類繼承父類例項和原型上的屬性/方法

實現方式一(所謂的寄生組合繼承——上述的結合)

  • 這裡我就不羅列程式碼了,相信文章前的你一定能通過上述來實現

實現方法二(ES6的class語法糖)

class Animal{
    static address(){ // 靜態方法(只能原型呼叫) 在ES7中可以直接新增靜態屬性 static address = {location: '大山'}
        return {location: '大山'}
    }
    constructor(name){ // 例項屬性
        this.name = name;
        this.eat = '吃肉'
    }
    say(){ // 原型上的方法
        return '說話'
    }
}

class Tiger extends Animal{
    constructor(name){
        super(name)
        this.name = name;
        this.age = 5;
    }
    sleep(){
        return '睡覺'
    }
}
let tiger = new Tiger('老虎');
console.log(tiger) // Tiger { name: '老虎', eat: '吃肉', age: 5 }
console.log(tiger.say()) // 說話
console.log(tiger.sleep()) // 睡覺
console.log(Animal.address()) // { location: '大山' }
console.log(Tiger.address()) // { location: '大山' }
複製程式碼

相關文章