在講幾種繼承方式的優缺點之前,先給大家介紹一下在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繼承方式