javascript原型繼承(第五篇)---幾種繼承方式的優缺點

在講幾種繼承方式的優缺點之前,先給大家介紹一下在javascript中對各種屬性的分類

在javascript中,數據分爲簡單數據和複雜數據,簡單數據在對象裏稱爲基本屬性,而數組,函數在對象裏稱爲引用屬性(數組,函數其實都是對象)

對此不太清楚的,可以參考我之前的博客 javascript數據類型

下面具體介紹一下在對象裏各種屬性的分類

function Fun(){
    // 私有屬性
    var val = 1;        // 私有基本屬性
    var arr = [1];      // 私有引用屬性
    function fun(){}    // 私有函數(引用屬性)

    // 實例屬性
    this.val = 1;               // 實例基本屬性
    this.arr = [1];             // 實例引用屬性
    this.fun = function(){};    // 實例函數(引用屬性)
}

// 原型屬性
Fun.prototype.val = 1;              // 原型基本屬性
Fun.prototype.arr = [1];            // 原型引用屬性
Fun.prototype.fun = function(){};   // 原型函數(引用屬性)

在看此篇博客之前,需要先看一下我的上一篇博客 javascript原型繼承(第四篇)—幾種繼承方式,且要看懂裏面的內容。

在看懂上一篇的基礎上,再來看這篇,便沒有什麼難度了,而我的本意也就是用這篇寫一下上一篇裏各個繼承方式的優缺點。

一、原型鏈繼承
function Super(){
    this.val = 1;
    this.arr = [1];
}
function Sub(){
    // ...
}
Sub.prototype = new Super();    // 核心
Sub.prototype.constructor = Sub;

var sub1 = new Sub();
var sub2 = new Sub();
sub1.val = 2;
sub1.arr.push(2);
alert(sub1.val);    // 2
alert(sub2.val);    // 1

alert(sub1.arr);    // 1, 2
alert(sub2.arr);    // 1, 2

優點:

1.簡單,易於實現

缺點:

1.修改sub1.arr後sub2.arr也變了,因爲來自原型對象的引用屬性是所有實例共享的。
(原因:執行sub1.arr.push(2);先對sub1進行屬性查找,找遍了實例屬性(在本例中沒有實例屬性),沒找到,就開始順着原型鏈向上找,拿到了sub1的原型對象,一搜身,發現有arr屬性。於是給arr末尾插入了2,所以sub2.arr也變了)

2.創建子類實例時,無法向父類構造函數傳參

二、構造函數繼承

原型鏈很簡單,可是存在2個致命缺點簡直不能用。於是,便有了這個構造函數繼承,

function Super(val){
    this.val = val;
    this.arr = [1];

    this.fun = function(){
        // ...
    }
}
function Sub(val){
    Super.call(this, arguments);   // 核心
    // ...
}

var sub1 = new Sub(1);
var sub2 = new Sub(2);
sub1.arr.push(2);
alert(sub1.val);    // 1
alert(sub2.val);    // 2

alert(sub1.arr);    // 1, 2
alert(sub2.arr);    // 1

alert(sub1.fun === sub2.fun);   // false,因爲sub1和sub2各自擁有fun,它們的內存地址是不同的

優點:

1.解決了子類實例共享父類引用屬性的問題
2.創建子類實例時,可以向父類構造函數傳參

缺點:

1.無法實現函數複用,每個子類實例都持有一個新的fun函數,如果實例太多,會造成內存大量被佔用

三、組合繼承

目前,我們的構造函數繼承仍舊有問題,所以便產生了組合繼承

function Super(){
    // 只在此處聲明基本屬性和引用屬性
    this.val = 1;
    this.arr = [1];
}
//  在此處聲明函數
Super.prototype.fun = function(){};
//Super.prototype.fun3...
function Sub(){
    Super.call(this);   // 核心
    // ...
}
Sub.prototype = new Super();    // 核心

var sub1 = new Sub(1);
var sub2 = new Sub(2);
sub1.val = 2;
sub1.arr.push(2);
alert(sub1.val);    // 2
alert(sub2.val);    // 1

alert(sub1.arr);    // 1, 2
alert(sub2.arr);    // 1
alert(sub1.fun === sub2.fun); //true 

優點:

1.不存在引用屬性共享問題
2.創建子類實例時,可以向父類構造函數傳參
3.函數可複用

缺點:(一點小瑕疵,但仍舊不影響它是現在最常用的繼承方式)

1.子類原型上有一份多餘的父類實例屬性,因爲父類構造函數被調用了兩次,生成了兩份,而子類實例上的那一份屏蔽了子類原型上的。

最後,javascript原型繼承到此就告一段落了,其實還有在js中還有很多繼承方式,不過我覺得了解並掌握了上述三種繼承方式,已經足夠縱橫天下了。


參考了相關博客夢燼–重新理解js繼承方式

發佈了32 篇原創文章 · 獲贊 23 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章