1. 程式人生 > >面向物件——原型與原型鏈、繼承

面向物件——原型與原型鏈、繼承

本文程式碼及內容來自於博覽網課程——WEB前端養成計劃的歸納總結,有興趣的小夥伴可以移步(以前沒有購買過的現在好像無法觀看了)

一、運用建構函式的方式來建立特定型別的物件

此方式呼叫建構函式會經歷以下4步:

(1)建立一個例項

(2)將建構函式的作用域賦給例項

(3)執行建構函式中的程式碼,為例項新增屬性

(4)返回例項(不需要return語句,預設返回)

問題:例項calendar1和calendar2的show()方法是不同的,會造成記憶體空間的浪費

解決方法:

(1)將show()方法設定為全域性方法??              X

非Calender物件的例項也能訪問到show()方法,不合理

(2)運用原型模式                                                

我們建立的每個函式都有一個prototype(原型)屬性,將方法定義在prototype中,當然,不要把屬性放在prototype中,否則例項會共享一個屬性

二、理解原型物件

只要建立了一個新的建構函式,就會為其建立一個prototype屬性,此屬性指向函式的prototype原型物件。預設情況下,原型物件會有一個constructor屬性指向所在函式。

alert(Calendar.prototype.constructor == Calendar)     //true

當呼叫建構函式建立一個例項後,該例項包含一個內部屬性(無法通過屬性名訪問),ECMAScript5將其命名為[[prototype]], ECMASript5可通過Object.gettPrototypeOf(obj)方法返回例項的原型物件,具體關係可看如下圖示:

將方法定義到prototype中有一個問題,即每次都要寫一遍Calendar.prototype。改進做法:用物件字面量來重寫prototype原型物件。

與之前的方法不同同,此時Calendar.prototype是一個新的物件,constructor不再指向Calendar,可以這樣調整:

三、原型的動態性

物件的屬性訪問實際上是在作用域鏈裡執行搜尋的過程,所以先建立了例項再修改原型也可正確執行,但用物件字面量重寫prototype原型物件的方法則會報錯。

           // ✔

重寫原型物件:

 → 

四、原型鏈

ECMAScript的繼承主要是依靠原型鏈來實現的,原型鏈的基本思想是利用原型讓一個型別繼承另一個型別的屬性和方法,把子類的原型物件設定為父類的例項,就形成了原型鏈。

// 父類
function SuperType() {
    this.property = true;
}
SuperType.prototype.getSuperValue = function() {
    return this.property;
}
// 子類
function SubType() {
    this.subProperty = false;
}
SubbType.property = new SuperType();            // 注意順序
SubType.property.getSubValue = function() {     // 不能顛倒
    return this.subProperty;
}
// 例項
var instance = new SubType();

SuperType

SuperType + SubType

我們沒有使用SubType預設提供的原型,而是將SuperType的例項作為它的新原型。

注:

1.通過原型鏈實現繼承時,不能使用物件字面量建立原型方法,這樣會重寫原型鏈

// 父類
function SuperType() {
    this.property = true;
}
SuperType.prototype.getSuperValue = function() {
    return this.property;
}
// 子類
function SubType() {
    this.subProperty = false;
}
SubbType.property = new SuperType();           
SubType.property = function() {             // 重寫了原型鏈,斷開了與父類的連線
    getSubValue: function() {
        return this.subProperty;
    }
}
// 例項
var instance = new SubType();

2.所有引用型別預設都通過原型鏈實現了對Object的繼承

五、繼承

1.借用建構函式:在子類的建構函式中呼叫父類的建構函式,eg:

function SubType() {
    SuperType.call(this, attr1, attr2, ...);       // 運用call方法改變作用域
    ......
}

2.組合繼承:將原型鏈和借用建構函式的技術組合到一起實現繼承。

但組合繼承會呼叫兩次父類的建構函式,存在一定隱患。

3.原型式繼承:

function object(O) {
    function F() {}        // (1)建立一個臨時性的建構函式,將傳入的物件作為建構函式的原型
    F.prototype = o;
    return new F();        // (2)返回臨時型別的例項
}    

ECMAScript5新增了Object.create()方法規範原型式繼承,傳一個引數時與自定義的object()方法相同

4.寄生組合式繼承:通過借用建構函式來繼承屬性,通過原型鏈的混成形式來繼承方法。

function inheritPrototype(SubType, SuperType) {
// 建立父類原型的一個副本
    var prototype = Object.create(SuperType.prototype); 
// 重寫了原型的constructor屬性的指向已經不正確了,需為其新增constructor屬性
    prototype.constructor = subType;
// 將新建的物件賦給子類的原型
    subType.prototype = prototype;
}