面向對象的程式設計-3-繼承

来源:http://www.cnblogs.com/miaowwwww/archive/2017/01/06/6257657.html
-Advertisement-
Play Games

寫在前面 都知道,當我們讀取一個對象的屬性或方法的時候,會優先在這個對象上面找,如果在這個對象上找不到就會遍歷他的原型,還沒找到? >原型的原型,又沒找到?-->繼續往上。。。 這便是原型鏈的功用。下麵,我探討了一下原型鏈的使用與擴展,依靠原型鏈實現繼承。 至於什麼是繼承? 我的理解是,一個對象可以 ...


寫在前面

  都知道,當我們讀取一個對象的屬性或方法的時候,會優先在這個對象上面找,如果在這個對象上找不到就會遍歷他的原型,還沒找到?--->原型的原型,又沒找到?-->繼續往上。。。

  這便是原型鏈的功用。下麵,我探討了一下原型鏈的使用與擴展,依靠原型鏈實現繼承。

  至於什麼是繼承? 我的理解是,一個對象可以直接使用另一個對象的屬性和方法。

 

  本文結構:

    1. 直接使用原型鏈
    2. 借用構造函數
    3. 組合繼承
    4. 原型式繼承
    5. 寄生式繼承
    6. 寄生組合式繼承

  其中的繼承方式層層遞進,不斷進化完善缺點。

  進化過程:  1 → 2 → 3↘

                → → 6

           4 → 5 ↗

一、直接使用原型鏈

  1.回顧我的上一遍文章中寫的,構造函數、原型、實例之間的關係::

    每一個構造函數都有prototype指針指向一個原型對象,原型對象有一個constructor指針指向構造函數,而實例通過[[prototype]]指針共用一個原型對象。

  2.原型鏈繼承的實現:把一個構造函數的原型對象等於另一種類型(另一種構造函數)的實例

function SuperType() {
    this.property = 'aaa';
}
SuperType.prototype.getSuperValue = function(){
    return this.property;
}

function SubType() {
    this.subproperty = 'bbb';
}
SubType.prototype = new SuperType();

SubType.prototype.getSubValue = function() {
    return this.subproperty;
}
var instance = new SubType();
console.log(instance)

  關係圖解

 

   執行的結果:SubType.prototype 指向了一個 SuperType 的實例。並通過 SubType.prototype.getSubValue 為實例添加了方法。

  當最終實例 instance 調用 getSuperValue 方法的時候。 先遍歷實例instance對象,沒找到--->搜索instance.prototype(也是SuperType的一個實例),還沒找到--->搜索instnce.prototype.prototype (也是SuperType.prototype);

  3.註意

    3.1 別忘記預設的原型

      原型鏈可以無限長(當然為了性能,不要搞太長)。但是去到最後必然是 Object.prototype

    3.2 確定原型與實例的關係:原型鏈中的出現的原型都算該實例的原型

       instance instanceOf SubType // true   

         SuperType.isPrototypeOf(instance) // true 

      3.3 謹慎地定義方法: 因為是修改了SubType的原型對象,因此必須在替換之後才定義原型上的方法: SubType.prototype.getSubTypeValue() 並且不能使用字面量,不然又換了一個新對象。

  4.直接使用原型鏈繼承的優缺點

     SuperType構造函數中若使用了引用類型值。就會放映在SubType的原型上。在原型上出現引用類型,容易被實例修改。影響所有的實例。

     使用 SubType 創建對象的時候,不能再給SuperType傳遞參數了。因為SubType.prototype 已經是一個SuperType的實例了。

 

 二、借用構造函數實現繼承

  為解決直接使用原型鏈繼承中不能給 SuperType 傳遞參數的問題以及引用類型值問題而生。

function SuperType(name) {
    this.colors = ['red','blue','green'];
    this.name = name;
}

function SubType(name) {
    SuperType.call(this, name);
}

var instance1 = new SubType('jody');
var instance2 = new SubType('miaowwwww');
instance1.colors.push('black');
console.log(instance1)
console.log(instance2);
console.log(instance2.name)

 

   首先要知道,構造函數只不過是在特定的環境(this)中執行代碼,併為它定義屬型而已。因此,大可以在SubType中執行以下SuperType,這樣一來,SubType 就獲取 SuperType 的屬性和方法。

   優點:解決了原型鏈繼承的引用類型問題,以及 傳遞參數的問題

   缺點:若僅僅使用借用構造函數,無法避免構造函數模式的問題,(方法都在實例中,方法的復用性降低了),並且instance1 並不是SuperType 的一個實例,無法使用instanceOf,isPrototypeOf.

 

三、組合繼承

  鑒於借用構造函數繼承的缺點,當然不會僅僅借用構造函數啦。

  組合繼承又稱偽經典繼承,結合了原型鏈和借用構造函數 兩種技術。(相信,大家看完借用構造函數的缺點就已經想到了)

function SuperType(name) {
    this.colors = ['red','blue','green'];
    this.name = name;
}

SuperType.prototype.sayName = function() {
    console.log(this.name);
}
function SubType(name, age) {
    SuperType.call(this, name);
    this.age = age;
}

// 繼承方法、
SubType.prototype = new SuperType();    // 為了使用SuperType.prototype上的方法,所以要添加
SubType.prototype.constructor = SubType; //new 的時候要調用這個constructor?並沒有,這個屬性只是標記這個對象是這個類型,多一個判斷方法而已。這裡僅僅是補全替換prototype導致的constructor的缺失
SubType.prototype.sayAge = function() {    // 在SubType原型上,同時也是SuperType的實例
    console.log(this.age);
}

var instance1 = new SubType('jody', 22);
console.log(instance1)

 

    這種方式確實完美解決了傳遞參數,引用類型,公共方法重用的缺點。同時也可以被 instanceOf 和 isPrototypeOf() 識別。

    但是它產生了新的問題:SubType實例含有(colors,name)屬性,SubType.prototype也有(colors,name)這部分屬性冗餘了。(這個問題將在 六、組合式繼承中解決)

    

 四、原型式繼承

// 原型式繼承即通過object函數實現
function object(o) {
    function F() {};
    F.prototype = o;
    return new F();
}

// 實例
var person = {
    name: 'miaowwwww',
    friends: ['aaa', 'bbb']
};

var person1 = object(person);
person1.name = 'jody';
person1.friends.push('ccc');
console.log(person1)

 

   原型式繼承跟原型鏈是一個理念(把原型指向另一種類型的對象(實例))。把空構造函數的原型指向一個對象,然後創建空構造函數的實例,並放回。

  4.1 object函數在es5 中得到了實現:Object.create(obj, defineProperties) :第二個參數而可選,設置屬性,及其描述,描述預設false

var person2 = Object.create(person, {
    name:{
        value: 'dd',
        writable: false
    },
    age: {
        value: 22
    }
})
console.log(person2)

  4.2 缺點:跟原型鏈一樣,是原型上的引用類型值

 

五、寄生式繼承

  5.1 寄生式是在原型式的基礎上的改進。   —_— 只是把定義實例屬性的邏輯放到一個函數裡面了而已

function craateAnother(original) {
    var clone = Object.create(original);
    clone.sayName = function() {
        console.log(this.name);
    };
    return clone;
}
var person = { name: 'jody', friends: ['aa','bb']};
var person1 = createAnother(person);

 

   5.2 缺點:公共的屬性和方法(如:sayName)不能復用。

 

六、寄生組合式繼承

   這是 三、組合繼承 和 五、寄生式繼承的技術組合。(組合繼承使用寄生式繼承修改了,組合繼承的缺點。

  1.先貼出組合繼承的代碼理解一下:

    組合繼承的缺點:兩次調用SuperType(),  導致的 SubType.prototype產生了冗餘的(name,colors...)等SuperType的屬性。

function SuperType(name) {
    this.name = name;
    this.colors = ['aa','bb'];
}
SuperType.prototype.sayName = function() {
    console.log(this.name);
}
function SubType(name, age) {
    SuperType.call(this, name);        //    第二次執行SuperType
    this.age = age;
}
SubType.prototype = new SuperType();    //第一次執行SuperType
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function() {
    console.log(this.age);
}

 

   既然知道 SubType.prototype = new SuperType(); 只是為了獲取SuperType.prototype。 那麼可以繞過執行SuperType,直接獲取SuperType.prototype。(這便是寄生式的作用)

  2 寄生式繼承的用處 : 使用Object.create() 創建一個空的實例,並且該實例的prototype 指向了 SuperType.prototype. (乾凈,無冗餘屬性)

function inheritPrototype(subType, superType) {
    var emptyInstance = Object.create(superType.prototype);
    emptyInstance.constructor = subType; //補充constructor的缺失
    subType.prototype = emptyInstance;
}

 

  3.寄生組合式繼承代碼

function inheritPrototype(subType, superType) {
    var prototype = Object.create(superType.prototype);
    prototype.constructor = subType; //補充constructor的缺失
    subType.prototype = prototype;
}
function SuperType(name) { this.name = name; this.colors = ['aa','bb']; } SuperType.prototype.sayName = function() { console.log(this.name); } function SubType(name, age) { SuperType.call(this, name); this.age = age; } inheritPrototype(SubType, SuperType); // SubType.prototype = SuperType.prototype; //不可以這麼做,因為sayAge會添加在SuperType.prototype中 SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function() { console.log(this.age); } var instance = new SubType('jody', 22); console.log(instance) console.log(instance.name)

 

 

   至此: 寄生組合式繼承被認為是引用類型最理想的繼承範式。

 

 備註:(接受指導與批評)

   本文是閱讀高級程式設計(第三版)P162~P174 的理解與總結。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 單例模式 代碼: 第一種: 第二種: 上面兩種單例模式中,基本思路相同,將構造方法私有,建立私有的實例對象,提供公有的對象。第一種方法,外部每次調用時都會判斷實例是否已經創建,第二種方法則是在一開始就將類的對象實例化好了,一直存在在記憶體中。區別也不大,單例的思想也就這麼簡單。這裡要說的是下麵這種方法 ...
  • 六、新建webpack配置文件 webpack.config.js 文件整體框架內容如下,後續會詳細說明每個配置項的配置 webpack.config.js直接放在項目demo目錄下 七、新建app文件夾,存放html+css+js index.html css/index.css js/index ...
  • 嗯,瀏覽器一般會設置,預設樣式,對於,我們來說,這樣很不方便,所以就有了樣式重置。 接下來我推薦兩個樣式重置的 小東西 resert 我這裡附上當前最新的版本,如有需要可以直接去,resert.com,下載 normalize.com 我推薦使用這個,因為還保留了某些標簽的特性,比如h1,h2,h3 ...
  • 誤區一.多div症 上述使用多餘的div標簽“多div症”,應簡化成下 誤區二.多類class症 註意點class可以應用於頁面任意多個元素,非常適合標識內容類型或其他相似的條目 一段新聞(新聞標題、新聞詳情內容)類news-head與news-text 多類症表現,不需要這麼多的類區分元素樣式 最 ...
  • 1.什麼是ajax? Ajax 是 Asynchronous JavaScript and XML(以及 DHTML 等)的縮寫。 2.ajax需要什麼基礎? HTML 用於建立 Web 表單並確定應用程式其他部分使用的欄位。 JavaScript 代碼是運行 Ajax 應用程式的核心代碼,幫助改進 ...
  • 寫在前面:以下是個人總結的關於js常見的入門級的問題一些總結。 js是有 ECMAScript Dom Bom 三部分組成。 1,undefined,NaN,Null,infinity 1) undefined 是undefined 類型 var a; //聲明變數後不賦值 typeof 類型判斷方 ...
  • #HTML 什麼是HTML,和他ML... 網頁可以比作一個裝修好了的,可以娶媳婦的房子. 房子分為:毛坯房,精裝修 毛坯房的修建: 磚,瓦,水泥,石頭,石子.... 精裝修的房子:在毛坯房的基礎上進行粉刷,覆蓋,雕琢,修飾等操作.... HTML標簽 相當於蓋毛坯房用的石子,磚瓦....:房子成功 ...
  • 為了減少概念混淆,應該把 =稱作(得到或者賦值),==稱作(相等), 稱作(嚴格相等) 相同點: 它們兩個運算符都允許任意類型的的操作數,如果操作數相等,返回true,否則返回false 不同: ==:運算符稱作相等,用來檢測兩個操作數是否相等,這裡的相等定義的非常寬鬆,可以允許進行類型轉換 :用來 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...