深入理解繼承

来源:http://www.cnblogs.com/zhoulifeng/archive/2017/09/16/7529990.html
-Advertisement-
Play Games

學習怎樣創建對象是理解面向對象編程的第一步,第二步是理解繼承。在傳統的面向對象編程語言中,類繼承其他類的屬性。然而,JS的繼承方式與傳統的面向對象編程語言不同,繼承可以發生對象之間,這種繼承的機制是我們已經熟悉的一種機制:原型。 1.原型鏈接和Object.prototype js內置的繼承方式被稱 ...


學習怎樣創建對象是理解面向對象編程的第一步,第二步是理解繼承。在傳統的面向對象編程語言中,類繼承其他類的屬性。
然而,JS的繼承方式與傳統的面向對象編程語言不同,繼承可以發生對象之間,這種繼承的機制是我們已經熟悉的一種機制:原型。

1.原型鏈接和Object.prototype

js內置的繼承方式被稱為原型鏈接(prototype chaining)原型繼承(prototypal inheritance)。正如我們在前一天所學的,原型對象上定義的屬性,在所有的對象實例中都是可用的,這就是繼承的一種形式。對象實例繼承了原型中的屬性。而原型也是一個對象,所以它也有自己的原型,並且繼承原型中的屬性。這被稱為原型鏈:對象繼承自己原型對象中屬性,而這個原型會繼續向上繼承自己的原型,依此類推。

所有對象,包括我們定義自己的對象,都自動繼承自Object,除非我們另有指定(本課後面討論)。更具體地說,所有對象都繼承Object.prototype。任何通過對象字面量定義的對象都有一個__proto__設置為object.prototype,意味著它們都繼承Object.prototype對象中的屬性,就像這個例子中的book

   var book = {
        title: "平凡的世界"
    };
    var prototype = Object.getPrototypeOf(book);
    console.log(prototype === Object.prototype);    // true

book的原型等於Object.prototype。不需要額外的代碼來實現這一點,因為這是創建新對象時的預設行為。這種關係意味著book自動接收來自Object.prototype對象中的方法。

  1.2.從Object.prototype中繼承的方法

我們在前幾天使用的一些方式實際上是定義在Object.prototype原型對象中,因此所有其他對象也都繼承了這些方法。這些方法是:

  • hasOwnProperty():判斷對象中有沒有某個屬性,接受一個字元串類型的屬性名作為參數。
  • propertyIsEnumerable():判斷對象中的某個屬性是否是可枚舉的。
  • isPrototypeOf():判斷一個對象是否是另個對象的原型。
  • valueOf:返回對象的值表示形式。
  • toString:返回對象的字元串表示形式。
  • toLocaleString: 返回對象的本地字元串表示形式。

這五種方法通過繼承所有對象都擁有這6個方法。當我們需要使對象在JavaScript中一致工作時,最後兩個是非常重要的,有時我們可能希望自己定義它們。

  1.3:valueOf()

當我們操作對象時,valueof()方法就會被調用時。預設情況下,valueof()簡單地返回對象實例。對於字元串,布爾值和數字類型的值,首先會使用原始包裝類型包裝成對象,然後再調用valueof()方法。同樣,Date對象的valueof()
方法返回以毫秒為單位的紀元時間(就像Date.prototype.getTime()一樣)。這也是為什麼我們可以對日期進行比較,例如:

    var now = new Date();
    var earlier = new Date(2010, 1, 1);
    console.log(now > earlier);           // true

  1.4修改Object.prototype

預設情況下,所有對象都繼承自Object.prototype,因此改變Object.prototype會影響到所有對象。這是非常危險的情況。

 Object.prototype.add = function(value) {
        return this + value;
    };
    var book = {
        title: "平凡的世界"
    };
    console.log(book.add(5));           // "[object Object]5"
    console.log("title".add("end"));    // "titleend"
    // in a web browser
    console.log(document.add(true));    // "[object HTMLDocument]true"
    console.log(window.add(5));         // "[object Window]true"

導致的另一個問題:

  var empty = {};
    for (var property in empty) {
        console.log(property);
    }

解決方法:

for(name in book){

    if(book.hasOwnProperty(name)){

        console.log(name);
    }
}

雖然這個方法可以有效地過濾掉我們不需要的原型屬性,但是它也限制了使用for-in只能變數的屬性,而不能遍歷原型屬性。建議不要修改原型對象。

2:對象繼承

最簡單的繼承方式是對象之間的繼承。我們所需要做的就是指定新創建對象的原型應該指向哪個對象。通過Object字面量的形式創建的對象預設將__proto__屬性指向了Object.prototype,但是我們可以通過Object.create()方法顯示地將__proto__屬性指向其他對象。

Object.create()方法接收兩個參數。第一個參數用來指定新創建對象的__proto__應該指向的對象。第二個參數是可選的,用來設置對象屬性的描述符(特性),語法格式與Object.definedProperties()方法參數個格式一樣。如下所示:

var book = {
        title: "人生"
    };

    // 等價於
    var book = Object.create(Object.prototype, {
        title: {
            configurable: true,
            enumerable: true,
            value: "人生",
            writable: true
        }
    });

代碼中兩個聲明的效果是一樣的。第一個聲明使用對象字面量的方式定義一個帶有單個屬性:title的對象。這個對象自動繼承自Object.prototype,並且屬性預設被設置成可配置,可枚舉,可寫。第二個聲明和第一個一樣,但是顯示使用了Object.create()方法。但是你可能永遠不會這樣顯示地直接繼承Object.prototype,沒有必要這樣做,因為預設就已經繼承了Object.prototype。繼承自其他對象會比較有趣一點:

var person1 = {
    name: '張三',
    sayName: function(){
        console.log(this.name);
    }
};

var person2 = Object.create(person1, {
    name: {
        value: '李四',
        configurable: true,
        enumerable: true,
        writable: true
    }
});

person1.sayName();      // '張三'
person2.sayName();      // '李四'

console.log(person1.hasOwnProperty("sayName"));     // true
console.log(person1.isPrototypeOf(person2));        // true
console.log(person2.hasOwnProperty("sayName"));     // false

這段代碼創建了一個對象person1,該對象有一個name屬性和一個sayName()方法。person2對象繼承了person1,因此它也繼承了name屬性和sayName()方法。然而,person2是通過Object.create()方法定義的,它也定義了自己的name屬性。對象自己的屬性遮擋了原型的中同名屬性name。因此,person1.sayName()輸出'張三'person2.sayName()輸出'李四'。記住,person2.sayName()只存在於person1中,被person2繼承了下來。

當對象的屬性被訪問時,JavaScript會首先會在對象的屬性中搜索,如果沒有找到,則繼續在__proto__指向的原型對象中搜索。如果任然沒有找到,則繼續搜索原型對象的上個原型對象,直到到達原型鏈的末端。原型鏈的末端結束於Object.prototypeObject.prototype對象的__proto__內部屬性為null

3.構造函數繼承

JavaScript中的對象繼承也是構造函數繼承的基礎。回顧昨天的內容,幾乎每一個函數都有一個可以修改或替換的prototype屬性。prototype屬性自動被賦值為一個新的對象,這個對象繼承自Object.prototype,並且對象中有一個自己的屬性constructor。實際上,JavaScript引擎為我們執行以下操作:

  // 這是我們寫的
    function YourConstructor() {
        // initialization
    }
    // JavaScript引擎在後臺幫我們做的: 
    YourConstructor.prototype = Object.create(Object.prototype, {
        constructor: {
            configurable: true,
            enumerable: true,
            value: YourConstructor
            writable: true
        }
    });

因此,不做任何額外的工作,這段代碼給我們的構造函數的prototype屬性設置了一個對象,這個對象繼承自Object.prototype,這意味著通過構造函數YourConstructor()創建的所有實例都繼承自Object.prototypeYourConstructorObject的子類,ObjectYourConstructor的超類。

由於prototype屬性是可寫的,因此通過覆寫它我們可以改變原型鏈。例如:

function Rectangle(length, width) {
        this.length = length;
        this.width = width;
    }
    Rectangle.prototype.getArea = function() {
        return this.length * this.width;
    };
    Rectangle.prototype.toString = function() {
        return "[Rectangle " + this.length + "x" + this.width + "]";
    };

    // 繼承 Rectangle
    function Square(size) {
        this.length = size;
        this.width = size;
    }
    Square.prototype = new Rectangle();
    Square.prototype.constructor = Square;
    Square.prototype.toString = function() {
        return "[Square " + this.length + "x" + this.width + "]";
    };
    var rect = new Rectangle(5, 10);
    var square = new Square(6);
    console.log(rect.getArea());                // 50
    console.log(square.getArea());              // 36
    console.log(rect.toString());               // "[Rectangle 5x10]"
    console.log(square.toString());             // "[Square 6x6]"
    console.log(rect instanceof Rectangle);     // true
    console.log(rect instanceof Object);        // true
    console.log(square instanceof Square);      // true
    console.log(square instanceof Rectangle);   // true
    console.log(square instanceof Object);      // true

這段代碼中有兩個構造函數:ReactangleSquareSquare構造函數的原型對象被重新賦值為Reactangle的對象實例。在創建Reactangle對象實例的時候沒有傳遞參數,因為它們沒有用,如果傳遞參數了,所有的Square對象實例都會共用相同的尺寸。以這種方式改變原型鏈之後,要確保constructor屬性的指向正確的構造函數。

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

-Advertisement-
Play Games
更多相關文章
  • ?useUnicode=true&characterEncoding=utf-8&useSSL=false ...
  • 【大聖看玩多時,問土地道:“此樹有多少株數?”土地道:“有三千六百株。前面一千二百株,花微果小,三千年一熟,人吃了成仙了道,體健身輕。中間一千二百株,層花甘實,六千年一熟,人吃了霞舉飛升,長生不老。後面一千二百株,紫紋緗核,九千年一熟,人吃了與天地齊壽,日月同庚。”大聖聞言,歡喜無任,當日查明瞭株樹 ...
  • 目前已定義的java設計模式細分下來有二十餘種,這篇博客主要是想從大家所熟知的孫悟空入手,闡述各個設計模式的概念和優缺點,以及他們之間的聯繫。 在下麵介紹的每個設計模式里,都會有與孫悟空相關的具體案例,主要是為了方便大家理解與記憶,否則使用一些枯燥的例子,很難讓人印象深刻。 按筆者的理解,設計模式的 ...
  • 1目的 規範開發模式過程,指導項目研發、質控測試、DevOps的相關活動。 2適用範圍 本規範的作用範圍是為互聯網軟體產品相關項目開發模式的管理過程。 (1) 對項目團隊中研發人員在開發模式過程中的活動、職責等方面進行了指導; (2) 對項目團隊中質控測試在開發模式過程中的活動、職責等方面進行了指導 ...
  • MySQL應用軟體以及環境安裝(Navicat軟體) 百度雲盤鏈接分享: 鏈接: https://pan.baidu.com/s/1o8UMiro 密碼: 8pdu ...
  • 定義: 定義一個操作中演算法的框架,而將一些步驟延遲到子類中。模板方法模式使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟。 UML類圖: 具體代碼: 模塊說明: AbstractClass:抽象類,定義整個流程演算法的骨架。ConcreteClass:具體類,實現流程演算法里的具體步驟。 ...
  • Keepalived實現雙機熱備: 雙機熱備一般指的是兩台機器設備同時在運行,但是只有一臺處於提供服務的狀態。當其中一臺設備出現故障,那麼另一臺設備接管角色,繼續提供服務,他們之間的切換時間非常之短,基本是無縫切換,所以避免了因為某台伺服器出現故障,導致系統軟體不能正常運行的問題。 · Keepal ...
  • Rafy是什麼可以通過下麵博客來瞭解 Rafy 領域實體框架演示(3) - 快速使用 C/S 架構部署 - BloodyAngel - 博客園 以下是看源碼中的一點記錄,供以後學習使用 主要是Rafy中IOC容器的定義,實現方式,用法 下麵先看一個圖: 這個圖片包含,三個方面的內容 下麵主要從IOb ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...