1.簡單原型鏈
function Super(){
this.val = 1;
this.arr = [1];
}
function Sub(){
// ...
}
Sub.prototype = new Super(); // 核心
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
優(yōu)點:簡單,易于實現
缺點:
- 修改sub1.arr后sub2.arr也變了,因為來自原型對象的引用屬性是所有實例共享的。
可以這樣理解:執(zhí)行sub1.arr.push(2);先對sub1進行屬性查找,找遍了實例屬性(在本例中沒有實例屬性),沒找到,就開始順著原型鏈向上找,拿到了sub1的原型對象,一搜身,發(fā)現有arr屬性。于是給arr末尾插入了2,所以sub2.arr也變了 - 創(chuàng)建子類實例時,無法向父類構造函數傳參
2.借用構造函數
function Super(val){
this.val = val;
this.arr = [1];
this.fun = function(){
// ...
}
}
function Sub(val){
Super.call(this, val); // 核心
// ...
}
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
優(yōu)點:
解決了子類實例共享父類引用屬性的問題
創(chuàng)建子類實例時,可以向父類構造函數傳參
缺點:
- 無法實現函數復用,每個子類實例都持有一個新的fun函數,太多了就會影響性能,內存爆炸。。
3.組合繼承(最常用)
function Super(){
// 只在此處聲明基本屬性和引用屬性
this.val = 1;
this.arr = [1];
}
// 在此處聲明函數
Super.prototype.fun = function(){};
function Sub(){
Super.call(this); // 核心
// ...
}
Sub.prototype = new Super(); // 核心
var sub1 = new Sub(1);
var sub2 = new Sub(2);
alert(sub1.fun === sub2.fun); // true
優(yōu)點:把實例函數都放在原型對象上,以實現函數復用。同時還要保留借用構造函數方式的優(yōu)點。
缺點:子類原型上有一份多余的父類實例屬性,因為父類構造函數被調用了兩次,生成了兩份,而子類實例上的那一份屏蔽了子類原型上的。
4.寄生組合繼承(最佳方式)
function beget(obj){
var F = function(){};
F.prototype = obj;
return new F();
}
function Super(){
// 只在此處聲明基本屬性和引用屬性
this.val = 1;
this.arr = [1];
}
// 在此處聲明函數
Super.prototype.fun1 = function(){};
Super.prototype.fun2 = function(){};
//Super.prototype.fun3...
function Sub(){
Super.call(this); // 核心
// ...
}
var proto = beget(Super.prototype); // 核心
proto.constructor = Sub; // 核心
Sub.prototype = proto; // 核心
var sub = new Sub();
alert(sub.val);
alert(sub.arr);
優(yōu)點:切掉了原型對象上多余的那份父類實例屬性。
缺點:出來寫起來麻煩一點,沒啥毛病!
5.原型式和6.寄生式就是寄生組合繼承的一種過度關系,不詳細說明了。
具體詳解可見參考資料:
- JavaScript高級程序設計》
- JavaScript語言精粹
- Javascript 面向對象編程-阮一峰大神
- 重新理解JS的6種繼承方式