JavaScript經常使用6大繼承方式解析

原型鏈繼承

//父類
function Person(name, age) {
   this.name = name;
   this.age = age;
   this.play = [1, 2, 3];
   this.setName = function () { }
}
Person.prototype.setAge = function () { }
//子類
function Student(price) {
   this.price = price;
   this.setScore = function () { }
}
Student.prototype = new Person() // 核心,子類型的原型爲父類型的一個實例對象
var s1 = new Student(15000)
var s2 = new Student(14000)
console.log(s1,s2)
  • 說明:
    實現的本質是經過將子類的原型指向了父類的實例。因此子類的實例就能夠經過__proto__訪問到 Student.prototype 也就是Person的實例,
    這樣就能夠訪問到父類的私有方法,而後再經過__proto__指向父類的prototype就能夠得到到父類原型上的方法。
    子類繼承父類的屬性和方法是將父類的私有屬性和公有方法都做爲本身的公有屬性和方法。
  • 特色:
    父類新增原型方法/原型屬性,子類都能訪問到
    簡單,易於實現
  • 缺點:
    沒法實現多繼承
    來自原型對象的全部屬性被全部實例共享
    建立子類實例時,沒法向父類構造函數傳參
    要想爲子類新增屬性和方法,必需要在Student.prototype = new Person() 以後執行,不能放到構造器中

構造函數繼承

function Person(name, age) {
    this.name = name,
    this.age = age,
    this.setName = function () {}
}
Person.prototype.setAge = function () {}
function Student(name, age, price) {
    Person.call(this, name, age)  // 核心,至關於: this.Person(name, age)
    /*this.name = name
    this.age = age*/
    this.price = price
}
var s1 = new Student('Tom', 20, 15000)
  • 說明:
    就是將子類中德變量在父類中執行一遍。只能繼承父類的屬性和方法,若是父類的原型還有方法和屬性,子類是拿不到的。
  • 特色:
    解決了原型鏈繼承中子類實例共享父類引用屬性的問題
    建立子類實例時,能夠向父類傳遞參數
    能夠實現多繼承(call多個父類對象)
  • 缺點:
    實例並非父類的實例,只是子類的實例
    只能繼承父類的實例屬性和方法,不能繼承原型屬性和方法
    沒法實現函數複用,每一個子類都有父類實例函數的副本,影響性能

組合繼承

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.setAge = function () { }
}
Person.prototype.setAge = function () {
    console.log("111");
}
function Student(name, age, price) {
    Person.call(this,name,age);//核心
    this.price = price;
    this.setScore = function () { }
}
Student.prototype = new Person();//核心
Student.prototype.constructor = Student//核心,組合繼承是須要修復構造函數指向的
Student.prototype.sayHello = function () { }
var s1 = new Student('Tom', 20, 15000);
var s2 = new Student('Jack', 22, 14000);
console.log(s1);
console.log(s1.constructor); //Student
console.log(s2.constructor); //Person
  • 說明:
    融合原型鏈繼承和構造函數的優勢,並過濾掉其缺點,是 JavaScript 中最經常使用的繼承模式。
    先在使用構造函數繼承時執行一遍父類的構造函數,又在實現子類原型的原型鏈繼承時又調用一遍父類構造函數。
  • 優勢:
    能夠繼承實例屬性/方法,也能夠繼承原型屬性/方法
    不存在引用屬性共享問題
    可傳參
    函數可複用
  • 缺點:
    調用了兩次父類構造函數,生成了兩份實例

原型式繼承

function createObj(o) {
    function F(){}
    F.prototype = o;
    return new F();
}
  • 說明:
    其實是對原型鏈繼承的一個封裝,也是ES5 Object.create 的模擬實現,將傳入的對象做爲建立的對象的原型。
  • 特色:
    父類新增原型方法/原型屬性,子類都能訪問到
    簡單,易於實現
  • 缺點:
    包含引用類型的屬性值始終都會共享相應的值,這點跟原型鏈繼承同樣。

寄生式繼承

function createObj (o) {
    var clone = Object.create(o);
    clone.sayName = function () {
        console.log('hi');
    }
    return clone;
}
  • 說明:
    實際上是對原型式繼承的第二次封裝,過程當中對繼承的對象進行了拓展。
  • 特色:
    跟借用構造函數模式同樣,每次建立對象都會建立一遍方法。
  • 缺點:

寄生組合式繼承

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

function prototype(child, parent) {
    var prototype = object(parent.prototype);
    prototype.constructor = child;
    child.prototype = prototype;
}

// 使用時
prototype(Child, Parent);
  • 說明:
    解決了組合繼承存在的問題
  • 特色:
    只調用了一次 Parent 構造函數,而且所以避免了在 Parent.prototype 上面建立沒必要要的、多餘的屬性
    原型鏈還能保持不變
    還可以正常使用 instanceof 和 isPrototypeOf
  • 缺點:

參考資料

相關文章
相關標籤/搜索