一個例子-看盡JS繼承

一個系列: 一個列子看盡一個知識點

1. 首先我們來個簡單的,ES6中寫一個繼承

是不是非常的開心,這是在寫C#嗎?JS的繼承也如此簡單哦,那還看盡啥呢

class father {
}
class child extends father {
}

2. 但是這是ES6呢,在babel轉(zhuǎn)成ES5后的代碼呢,如下,我們來好好看下呢

"use strict";

// 獲取對象上面的屬性
var _get = function get(_x, _x2, _x3) {
    var _again = true;
    // 循環(huán)往上查找屬性
    _function: while (_again) {
        var object = _x, property = _x2, receiver = _x3;
        _again = false;
        if (object === null) // 為空就變回原來的親生父母Function調(diào)用
            object = Function.prototype;
        var desc = Object.getOwnPropertyDescriptor(object, property); // 獲取屬性的描述,比如constructor的描述
        if (desc === undefined) { // 沒有屬性描述,則往原型上找,就是小蝌蚪找媽媽
            var parent = Object.getPrototypeOf(object);
            if (parent === null) {
                return undefined;
            } else {
                _x = parent;
                _x2 = property;
                _x3 = receiver;
                _again = true;
                desc = parent = undefined;
                continue _function;
            }
        } else if ("value" in desc) { // 屬性描述里面的value就是這個屬性的指向,返回
            return desc.value;
        } else {
            var getter = desc.get;
            if (getter === undefined) {
                return undefined;
            }
            return getter.call(receiver);
        }
    }
};

// 類的繼承
function _inherits(subClass, superClass) {
    // 判斷了一下類型檢查,否則拋錯
    if (typeof superClass !== "function" && superClass !== null) {
        throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
    }

    // 作為一個函數(shù)的原型屬性繼承!?。?    // 以父類原型屬性為模板,創(chuàng)建對象給子類的原型屬性,同時把函數(shù)的constructor指向了自己
    // 這樣子類通過這種方式繼承了父類的原型屬性,有上面的東西啦
    // Object.create標明這里子類和父類的原型屬性不是同一個,新創(chuàng)建了一個
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: { // 更改原有函數(shù)的constructor的指向,以后我生出的孩兒,都要知道我才是他們媽媽
            value: subClass, // 你看,指向了自己了吧
            enumerable: false, // 不可枚舉
            writable: true, // 可寫
            configurable: true // 可配置
        }
    });

    // 作為一個對象的原型指向繼承?。?!
    // 徹底改變子類原型,指向父類,標明我是父類創(chuàng)建噠, Object.setPrototypeOf這個是ES6的語法,等同于subClass.__proto__ 
    // 那么如果這里不設(shè)置的話,我們可以看到subClass.__proto__ === Function.prototype, 還是由原生Function創(chuàng)建的
    // 所以這里的作用就是徹底撇清和親生父母Function的關(guān)系,徹底投靠給了養(yǎng)父母SuperClass, 你通過__proto__查戶口本我也是superClass的兒子
    // 當然subClass.__proto__.__proto__ === superClass.__proto__ === Function.prototype 這下你爺爺變成Function了
    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}

// 判斷是否是繼承此類的實例
function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
    }
}

// 1. 父類的函數(shù)聲明
var father = function father() {
    _classCallCheck(this, father);
};

// 2. 子類的函數(shù)聲明
var child = (function (_father) {
    // 3. 繼承
    _inherits(child, _father);

    // 4. 返回真實函數(shù)聲明
    function child() {
        // 5. 判斷調(diào)用者是否是當前實例
        _classCallCheck(this, child);

        // 6. 調(diào)用父類上的constructor構(gòu)造函數(shù)來創(chuàng)建實例,就是調(diào)用父類函數(shù)創(chuàng)建對象
        //    Object.getPrototypeOf(child.prototype) === child.prototype.__proto__ :這里是找原型屬性的原型,找到father啦
        //    為什么能找到呢, Object.setPrototypeOf(subClass, superClass) , 就是這句話的效果撒
        //    一開始以為是自己的原型,都快被繞暈了
        _get(Object.getPrototypeOf(child.prototype), "constructor", this).apply(this, arguments);
    }
    return child;
})(father);

  • 哇,好長,這個代碼還是得從最后看起,注意大概步驟分為6步,仔細看源碼注釋哦!
  1. 父類的函數(shù)聲明,這里很簡單了,原來類就是一個function啊,雖然我早就知道了,里面只有一句來驗證用father來創(chuàng)建對象的時候,this是不是father的實例
var father = function father() {
    _classCallCheck(this, father);
};
  1. 子類的函數(shù)聲明,我們看最外面,變成一個閉包了喲,傳入的參數(shù)是father,因為我們指定是繼承father的嘛,所以在new Child()的時候需要father, 這里就把father先存起來
var child = (function (_father) {
    _inherits(child, _father);
    function child() {
        _classCallCheck(this, child);
        _get(Object.getPrototypeOf(child.prototype), "constructor", this).apply(this, arguments);
    }
    return child;
})(father);
  1. 繼承:上面說存起了father的嘛,存起來就是為了繼承的喲,就是這句 _inherits(child, _father);小小一句話是怎么實現(xiàn)的呢,看這個函數(shù)如下
function _inherits(subClass, superClass) {
    if (typeof superClass !== "function" && superClass !== null) {
        throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: { // 更改原有函數(shù)的constructor的指向,以后我生出的孩兒,都要知道我才是他們媽媽
            value: subClass, // 你看,指向了自己了吧
            enumerable: false, // 不可枚舉
            writable: true, // 可寫
            configurable: true // 可配置
        }
    });
    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}

這里最重要的兩步,

  • 作為一個函數(shù)的原型屬性繼承!??!
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: { // 更改原有函數(shù)的constructor的指向,以后我生出的孩兒,都要知道我才是他們媽媽
            value: subClass, // 你看,指向了自己了吧
            enumerable: false, // 不可枚舉
            writable: true, // 可寫
            configurable: true // 可配置
        }
    });

以父類原型屬性為模板,創(chuàng)建對象給子類的原型屬性,同時把函數(shù)的constructor指向了自己,這樣子類通過這種方式繼承了父類的原型屬性,有上面的東西啦,Object.create標明這里子類和父類的原型屬性不是同一個,新創(chuàng)建了一個

  • 作為一個對象的原型指向繼承?。?!
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;

徹底改變子類原型,指向父類,標明我是父類創(chuàng)建噠, Object.setPrototypeOf這個是ES6的語法,等同于subClass.proto ,那么如果這里不設(shè)置的話,我們可以看到subClass.proto === Function.prototype, 還是由原生Function創(chuàng)建的,所以這里的作用就是徹底撇清和親生父母Function的關(guān)系,徹底投靠給了養(yǎng)父母SuperClass, 你通過proto查戶口本我也是superClass的兒子.當然subClass.proto.proto === superClass.proto === Function.prototype 這下你爺爺變成Function了

  1. 返回真實函數(shù)聲明,這句簡單,返回了child函數(shù),跟father一樣,還是一個函數(shù)呀

  2. 判斷調(diào)用者是否是當前實例,同樣跟父類一樣

  3. 調(diào)用父類上的constructor構(gòu)造函數(shù)來創(chuàng)建實例,就是調(diào)用父類函數(shù)創(chuàng)建對象

_get(Object.getPrototypeOf(child.prototype), "constructor", this).apply(this, arguments);

注意這里的Object.getPrototypeOf(child.prototype) 等價于 child.prototype.__proto__ 等價于 father.prototype , 這句明白了才好理解呢,想用this調(diào)用父類上的構(gòu)造函數(shù)來創(chuàng)建象,所以繼承后你創(chuàng)建一個對象,真正是一層一層往上調(diào)用構(gòu)造函數(shù)來給你創(chuàng)建對象滴,具體_get里怎么拿到構(gòu)造函數(shù)就看源碼啦,

3. 最后附上博主幸幸苦苦畫的繼承關(guān)系圖呢,是不是很清晰明了了呢,對了,這里還有js深層的繼承關(guān)系呢,所謂一生二,二生三,三生萬物,這里的一就是null啊,喜歡給個贊喲?。?!

Javascript繼承詳解.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 繼承6種套餐 參照紅皮書,JS繼承一共6種 1.原型鏈繼承 核心思想:子類的原型指向父類的一個實例 Son.pro...
    燈不梨喵閱讀 3,251評論 1 2
  • 第一章 錯誤處理: 錯誤: 程序運行過程中,導(dǎo)致程序無法正常執(zhí)行的現(xiàn)象(即bug) 現(xiàn)象: 程序一旦出錯,默認會報...
    fastwe閱讀 1,249評論 0 1
  • 原文鏈接:zhuanlan.zhihu.com (一) 原型鏈繼承: function Parent(name) ...
    越努力越幸運_952c閱讀 332評論 0 2
  • 面向?qū)ο蟮恼Z言都有一個類的概念,通過類可以創(chuàng)建多個具有相同方法和屬性的對象,ES6之前并沒有類的概念,在ES6中引...
    Erric_Zhang閱讀 1,206評論 1 4
  • 講完上一張的皇后牌自然就到了皇帝,又名國王牌。 這是一個愛拼搏,注重事業(yè)而且野心勃勃的男人,對權(quán)力非??粗?,(不然...
    十七Regina閱讀 708評論 0 1

友情鏈接更多精彩內(nèi)容