JavaScript 中的六種繼承方式

直接進入主題:javascript

繼承的操做須要有一個父類,這裏使用構造函數外加原型來建立一個:html

// super
function Person(name){
    this.name = name;
}
Person.prototype.job = 'frontend';
Person.prototype.sayHello = function() {
    console.log('Hello '+this.name);
}
var person = new Person('jia ming');
person.sayHello(); // Hello jia ming
複製代碼

原型鏈繼承

// 原型鏈繼承
function Child() {
    this.name = 'child';
}
Child.prototype = new Person();
var child = new Child();
console.log(child.job); // frontend
// instanceof 判斷元素是否在另外一個元素的原型鏈上
// child是Person類的實例
console.log(child instanceof Person); // true
複製代碼

關鍵點:子類原型等於父類的實例Child.prototype = new Person()java

原型鏈的詳細講解本身以前有一篇文章說到深刻理解原型對象和原型鏈git

特色github

  1. 實例可繼承的屬性有:實例的構造函數的屬性,父類構造函數的屬性,父類原型上的屬性。(新實例不會繼承父類實例的屬性)

注意事項app

  1. 新實例沒法向父類構造函數傳參
  2. 繼承單一
  3. 全部新實例都會共享父類實例的屬性。(原型上的屬性是共享的,一個實例修改了原型屬性,另外一個實例的原型屬性也會被修改)

借用構造函數

// 借用構造函繼承
function Child() {
    Person.call(this, 'reng');
}
var child = new Child();
console.log(child.name); // reng
console.log(child instanceof Person); // false
child.sayHello(); // 報錯,繼承不了父類原型上的東西
複製代碼

關鍵點:用callapply將父類構造函數引入子類函數(在子類函數中作了父類函數的自執行(複製))Person.call(this, 'reng')frontend

針對call, apply, bind的使用,以前有篇文章談談JavaScript中的call、apply和bind提到。函數

特色post

  1. 只繼承了父類構造函數的屬性,沒有繼承父類原型的屬性
  2. 解決了原型鏈繼承的注意事項(缺點)1,2,3
  3. 能夠繼承多個構造函數的屬性(call能夠多個)
  4. 在子實例中能夠向父實例傳參

注意事項ui

  1. 只能繼承父類構造函數的屬性
  2. 沒法實現構造函數的複用。(每次用每次都要從新調用)
  3. 每一個新實例都有構造函數的副本,臃腫

組合繼承

組合繼承是原型鏈繼承和借用構造函數繼承的組合。

// 組合繼承
function Child(name) {
    Person.call(this, name);
}
Child.prototype = new Person();
var child = new Child('jia');
child.sayHello(); // Hello jia
console.log(child instanceof Person); // true
複製代碼

關鍵點:結合了兩種模式的優勢--向父類傳參(call)和複用(prototype)

特色

  1. 能夠繼承父類原型上的屬性,能夠傳參,可複用
  2. 每一個新實例引入的構造函數屬性是私有的

注意事項

  1. 調用了兩次父類的構造函數(耗內存)
  2. 子類的構造函數會代替原型上的那個父類構造函數(call至關於拿到了父類構造函數的副本)

原型式繼承

// 先封裝一個函數容器,用來承載繼承的原型和輸出對象
function object(obj) {
    function F() {}
    F.prototype = obj;
    return new F();
}
var super0 = new Person();
var super1 = object(super0);
console.log(super1 instanceof Person); // true
console.log(super1.job); // frontend
複製代碼

關鍵點:用一個函數包裝一個對象,而後返回這個函數的調用,這個函數就變成了能夠隨意增添屬性的實例或對象。Object.create()就是這個原理。

特色

  1. 相似於複製一個對象,用函數來包裝

注意事項

  1. 全部的實例都會繼承原型上的屬性
  2. 沒法實現複用。(新實例屬性都是後面添加的)

**Object.create()方法規範了原型式繼承。**這個方法接收兩個參數,一個用做新對象原型的對象和(可選的)一個爲新對象定義額外屬性的對象。

// 傳一個參數的時候
var anotherPerson = Object.create(new Person());
console.log(anotherPerson.job); // frontend
console.log(anotherPerson instanceof Person); // true
複製代碼
// 傳兩個參數的時候
var anotherPerson = Object.create(new Person(), {
    name: {
        value: 'come on'
    }
});
anotherPerson.sayHello(); // Hello come on
複製代碼

寄生式繼承

function object(obj) {
    function F(){}
    F.prototype = obj;
    return new F();
}
var sup = new Person();
// 以上是原型式繼承,給原型式繼承再套個殼子傳遞參數
function subobject(obj) {
    var sub = object(obj);
    sub.name = 'ming';
    return sub;
}
var sup2 = subobject(sup);
// 這個函數通過聲明後就成了可增添屬性的對象
console.log(sup2.name); // 'ming'
console.log(sup2 instanceof Person); // true
複製代碼

關鍵點:就是給原型式繼承外面套個殼子。

特色

  1. 沒有建立自定義類型,由於只是套了個殼子,返回對象,這個函數瓜熟蒂落就成了建立的新對象。

注意事項

  1. 沒用到原型,沒法複用

寄生組合繼承

它跟組合繼承同樣,都比較經常使用。

寄生:在函數內返回對象而後調用

組合

  1. 函數的原型等於另外一個實例
  2. 在函數中用apply或call引入另外一個構造函數,可傳參
// 寄生
function object(obj) {
    function F(){}
    F.prototype = obj;
    return new F();
}
// object是F實例的另外一種表示方法
var obj = object(Person.prototype);
// obj實例(F實例)的原型繼承了父類函數的原型
// 上述更像是原型鏈繼承,只不過只繼承了原型屬性

// 組合
function Sub() {
    this.age = 100;
    Person.call(this); // 這個繼承了父類構造函數的屬性
} // 解決了組合式兩次調用構造函數屬性的特色

// 重點
Sub.prototype = obj;
console.log(Sub.prototype.constructor); // Person
obj.constructor = Sub; // 必定要修復實例
console.log(Sub.prototype.constructor); // Sub
var sub1 = new Sub();
// Sub實例就繼承了構造函數屬性,父類實例,object的函數屬性
console.log(sub1.job); // frontend
console.log(sub1 instanceof Person); // true
複製代碼

重點:修復了組合繼承的問題

在上面的問題中,你可能發現了這麼一個註釋obj.constructor = Sub; // 必定要修復實例。爲何要修正子類的構造函數的指向呢?

由於在不修正這個指向的時候,在獲取構造函數返回的時候,在調用同名屬性或方法取值上可能形成混亂。好比下面:

function Car() { }
Car.prototype.orderOneLikeThis = function() {  // Clone producing function
    return new this.constructor();
}
Car.prototype.advertise = function () {
    console.log("I am a generic car.");
}

function BMW() { }
BMW.prototype = Object.create(Car.prototype);
BMW.prototype.constructor = BMW;              // Resetting the constructor property
BMW.prototype.advertise = function () {
    console.log("I am BMW with lots of uber features.");
}

var x5 = new BMW();

var myNewToy = x5.orderOneLikeThis();

myNewToy.advertise(); // => "I am BMW ..." if `BMW.prototype.constructor = BMW;` is not 
                      // commented; "I am a generic car." otherwise.
複製代碼

參考 & 後話

更多的內容,請移步個人博客,能給個贊就更好了😄

相關文章
相關標籤/搜索