1. 程式人生 > >深入理解 Javascript Prototype 原型繼承

深入理解 Javascript Prototype 原型繼承

什麼是prototype:

js物件定義函式物件中有一個prototype屬性,prototype屬性又指向了一個prototype物件,注意prototype屬性與prototype物件是兩個不同的東西,要注意區別。在prototype物件中又有一個constructor屬性,這個constructor屬性同樣指向一個constructor物件,而這個constructor物件恰恰就是這個function函式本身。

第一種繼承方式:

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

優點:易於實現
缺點:
    (1)不能進行深拷貝,因為他們共享父類Super物件的屬性與方法。
    (2)建立子類例項時,無法向父類建構函式傳參

 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

優點:
    (1)解決了子類例項共享父類引用屬性的問題。
    (2)建立子類例項時,可以向父類建構函式傳參。
缺點:無法實現函式複用,每個子類例項都持有一個新的fun函式,,會佔用更多記憶體,記憶體佔用多了就會影響效能。

3、組合繼承(最常用)——把例項函式都放在原型物件上,以實現函式複用。同時還要保留借用建構函式方式的優點,通過Super.call(this);繼承父類的基本屬性和引用屬性並保留能傳參的優點;通過Sub.prototype = new Super();繼承父類函式,實現函式複用

具體實現:

function Super(){
    // 只在此處宣告基本屬性和引用屬性
    this.val = 1;
    this.arr = [1];
}
//  在此處宣告函式
Super.prototype.fun1 = function(){};
Super.prototype.fun2 = function(){};
//Super.prototype.fun3...
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

優點:
(1)不存在引用變數共享問題
(2)可傳參
(3)函式可複用
缺點:(一點小瑕疵)子類原型上有一份多餘的父類例項屬性,因為父類建構函式被呼叫了兩次,生成了兩份,而子類例項上的那一份遮蔽了子類原型上的。。。又是記憶體浪費

4、原型式——用生孩子函式得到得到一個“純潔”的新物件(“純潔”是因為沒有例項屬性),再逐步增強之(填充例項屬性)

具體實現:

function beget(obj){   // 生孩子函式 beget:龍beget龍,鳳beget鳳。
    var F = function(){};
    F.prototype = obj;
    return new F();
}
function Super(){
    this.val = 1;
    this.arr = [1];
}
 
// 拿到父類物件
var sup = new Super();
// 生孩子
var sub = beget(sup);   // 核心
// 增強
sub.attr1 = 1;
sub.attr2 = 2;
//sub.attr3...
 
alert(sub.val);     // 1
alert(sub.arr);     // 1
alert(sub.attr1);   // 1

 優點:從已有物件衍生新物件,不需要建立自定義型別(更像是物件複製,而不是繼承。。)
缺點:
(1)原型引用屬性會被所有例項共享,因為是用整個父類物件來充當了子類原型物件,所以這個缺陷無可避免
(2)無法實現程式碼複用(新物件是現取的,屬性是現添的,都沒用函式封裝,怎麼複用)

5、寄生式——給原型式繼承穿了個馬甲而已,看起來更像繼承了

具體實現:

function beget(obj){   // 生孩子函式 beget:龍beget龍,鳳beget鳳。
    var F = function(){};
    F.prototype = obj;
    return new F();
}
function Super(){
    this.val = 1;
    this.arr = [1];
}
function getSubObject(obj){
    // 建立新物件
    var clone = beget(obj); // 核心
    // 增強
    clone.attr1 = 1;
    clone.attr2 = 2;
    //clone.attr3...
 
    return clone;
}
 
var sub = getSubObject(new Super());
alert(sub.val);     // 1
alert(sub.arr);     // 1
alert(sub.attr1);   // 1

優點:還是不需要建立自定義型別
缺點:無法實現函式複用(沒用到原型,當然不行)

6、寄生組合繼承(最佳方式)——用beget(Super.prototype);切掉了原型物件上多餘的那份父類例項屬性

具體實現:

function beget(obj){   // 生孩子函式 beget:龍beget龍,鳳beget鳳。
    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);

優點:可以說已經很完美了
缺點:複雜不易於理解
 


                                                                                                                                                      如需交流請加QQ,歡迎大家
                                                                                                                                                                                2952632928