JavaScript原型系列(二)什麼是原型繼承

將欲歙之,必固張之;將欲弱之,必固強之;——《道德經》javascript

JavaScript原型系列(一)構造函數、原型和原型鏈html

JavaScript原型系列(二)什麼是原型繼承java

JavaScript原型系列(三)Function、Object、Null等等的關係和雞蛋問題編程

簡介


JavaScript-prototype

在上一節上面介紹了原型和原型鏈,即每一個對象擁有一個原型對象,經過 __proto__ 指針指向上一個原型 ,並從中繼承方法和屬性,同時原型對象也可能擁有原型,這樣一層一層,最終指向 null,這種關係被稱爲原型鏈(prototype chain)app

繼承是面向對象編程語言的一大核心功能點,JavaScript是面向對象的只不過是比較特殊的面向對象的語言。它不像Java是基於類的面向對象,而javaScript是基於prototype的面向對象。編程語言

會用一篇文章來介紹什麼面向對象,javascript是怎麼實現繼承封裝多態javascript面向對象的特殊之處。函數

原型鏈繼承


JavaScript 對象是動態的屬性「包」(指其本身的屬性)。JavaScript 對象有一個指向一個原型對象的鏈。當試圖訪問一個對象的屬性時,它不單單在該對象上搜尋,還會搜尋該對象的原型,以及該對象的原型的原型,依次層層向上搜索,直到找到一個名字匹配的屬性或到達原型鏈的末尾。post

原型鏈繼承的本質是重寫原型對象,代之以一個新類型的實例。將父類的實例做爲子類的原型。看下面代碼:ui

function SubType () {
        this.name = 'subtype';
    }
    SubType.prototype.getName = function () {
        return this.name;
    }

    function Sub () {}
    // 將父類的實例做爲子類的原型
    Sub.prototype = new SubType();
    var sub = new Sub();
    sub.constructor === SubType; // true
    Sub.prototype.constructor === SubType; // true
    sub.name = 'sub';
    console.log(sub.getName()); // sub
複製代碼

優勢this

  • 基於原型的方法全部子類均可以複用

缺點

  • 多個實例對引用類型的操做會被篡改
  • 子類型的原型上的 constructor 屬性被重寫了
  • 建立子類型實例時沒法向父類型的構造函數傳參

主要分析一下它的缺點暫時不分析它的優勢。

引用類型被修改

由於本質上每一個實例的__proto__都會指向構造函數的prototype,實例上都是保存了一個引用地址,因此當prototype中的引用類型修改全部實例都會被改變。在上面代碼基礎上修改以下:

function SubType () {
        this.name = 'subtype';
    }
    SubType.prototype.getName = function () {
        return this.name;
    }

    SubType.prototype.Arr = ['sub', 'subtype', 'Sub'];
    function Sub () {}
    // 將父類的實例做爲子類的原型
    Sub.prototype = new SubType();
    var sub = new Sub();
    var sub1 = new Sub();
    var sub2 = new Sub();

    sub2.Arr.push('push');

    sub1.Arr; // ["sub", "subtype", "Sub", "push"]
    sub2.Arr; // ["sub", "subtype", "Sub", "push"]
複製代碼

在構造函數SubType.prototype新增Arr屬性而且賦值爲['sub', 'subtype', 'Sub'],經過new關鍵字實例化兩個實例sub1sub1,當修改了sub2.Arr的時候,sub1.Arr的也會被影響。

實例constructor被重寫

子類型原型上的 constructor 屬性被重寫, 執行 Sub.prototype = new SubType() 後原型被覆蓋,Sub.prototype 上丟失了 constructor 屬性, Sub.prototype 指向了 SubType.prototype,而 SubType.prototype.constructor 指向了 SubType,因此 Sub.prototype.constructor 指向了 SubType。 以下圖所示:

JavaScript-prototype

function SubType () {
        this.name = 'subtype';
    }
    SubType.prototype.getName = function () {
        return this.name;
    }

    function Sub () {}
    // 將父類的實例做爲子類的原型
    Sub.prototype = new SubType();
    // 全部涉及到原型鏈繼承的繼承方式都要修改子類構造函數的指向,不然子類實例的構造函數會指向SuperType。
    Sub.prototype.constrcutor = Sub;
    var sub = new Sub();

    Sub.prototype.constrcutor === Sub; // true
    sub.__proto__.constrcutor === Sub; // true
複製代碼

經過Sub.prototype.constrcutor = Sub;Sub.prototype.constrcutor指向Sub,若是所示:

JavaScript-prototype

給子類型原型添加屬性和方法必須在替換原型以後,緣由在第二點已經解釋過了,由於子類型的原型會被覆蓋。

屬性遮蔽

Sub.prototype上添加getName方法,當調用sub上得getName時,訪問到的是Sub.prototype.getName而不是訪問到SubType.prototype.getName,這種狀況稱爲屬性遮蔽(property shadowing)

function SubType () {
        this.name = 'subtype';
    }
    SubType.prototype.getName = function () {
        return this.name;
    }

    function Sub () {}
    // 將父類的實例做爲子類的原型
    Sub.prototype = new SubType();
    // 全部涉及到原型鏈繼承的繼承方式都要修改子類構造函數的指向,不然子類實例的構造函數會指向SuperType。
    Sub.prototype.constrcutor = Sub;
    // 添加getName屬性
    Sub.prototype.getName = function () {
        return 'Sub.prototype.getName';
    }

    var sub = new Sub();

    sub.getName(); // Sub.prototype.getName
複製代碼

能夠經過__proto__調用原型鏈上的屬性便可。

console.log(sub.__proto__.__proto__.getName()); // undefined
複製代碼

實現一個new


function create () {
        // 建立一個空對象
        let obj = new Object();
        // 獲取第一個參數,構造函數
        let Preson = [].shift.call(arguments);
        // 連接該對象(即設置該對象的構造函數)到另外一個對象;
        obj.__proto__ = Preson.prototype;
        // 綁定this指向,執行構造函數
        let result = Preson.apply(obj, arguments);
        return typeof result === 'object' ? result : obj;
    }
複製代碼

在這裏很少作贅述了,詳細內容我另外一片博客javascript中實現一個本身的new

總結

  • 每一個對象擁有一個原型對象,經過__proto__ 指針指向上一個原型 ,並從中繼承方法和屬性,同時原型對象也可能擁有原型,這樣一層一層,最終指向 null,這種關係被稱爲 原型鏈
  • 當訪問一個對象的屬性 / 方法時,它不單單在該對象上查找,還會查找該對象的原型,以及該對象的原型的原型,一層一層向上查找,直到找到一個名字匹配的屬性 / 方法或到達原型鏈的末尾null
  • 原型鏈的構建依賴於 __proto__,一層一層最終連接到 null
  • instanceof 原理就是一層一層查找 __proto__,若是和 constructor.prototype 相等則返回 true,若是一直沒有查找成功則返回 false
  • 原型鏈繼承的本質是重寫原型對象,代之以一個新類型的實例

參考

對象原型

繼承與原型鏈

圖解原型鏈及其繼承優缺點

一篇文章理解 JS 繼承

相關文章
相關標籤/搜索