成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

由一篇ES6繼承文章引發(fā)對于super關(guān)鍵字的思考

mudiyouyou / 773人閱讀

摘要:舉個(gè)栗子中一段簡單的繼承代碼實(shí)現(xiàn)使用了,不會(huì)報(bào)錯(cuò)這段代碼非常簡單,在子類中使用了關(guān)鍵字,編譯時(shí)不會(huì)報(bào)錯(cuò),也可以正常執(zhí)行。參考資料從中的講原型鏈與繼承的靜態(tài)屬性和實(shí)例屬性

問題引入

最近一直在看原型繼承相關(guān)的東西,翻到這么一篇文章: 從ES6中的extends講js原型鏈與繼承

文中有一個(gè)點(diǎn)讓我很感興趣,箭頭函數(shù)在繼承過程中無法通過super關(guān)鍵字獲取,這是為什么呢?

前置知識(shí) MDN上關(guān)于super的介紹

The super keyword is used to access and call functions on an object"s parent - in MDN
大概有這么幾個(gè)關(guān)鍵點(diǎn):

子類中存在constructor方法的時(shí)候,需要調(diào)用super方法,并且需要在使用this關(guān)鍵字之前調(diào)用

super關(guān)鍵字可以用來調(diào)用父對象上的方法

可以使用super來調(diào)用父對象上的靜態(tài)方法

不可以使用delete來刪除super上的屬性

不可以復(fù)寫super對象上的只讀屬性

子類中是否必須主動(dòng)調(diào)用super方法?

我的看法是不需要。
網(wǎng)上有些文章(比如這篇)寫道:

因?yàn)槿舨粓?zhí)行super,則this無法初始化。

我的個(gè)人理解是,this是指代執(zhí)行上下文環(huán)境的,不存在無法初始化的情況。更準(zhǔn)確的說法是這樣:如果不使用super方法,那么父類中的屬性值無法進(jìn)行初始化,如果這個(gè)時(shí)候子類通過this字段來訪問了父類中的屬性值,那么只能得到一個(gè)undefined。至于為什么這么寫編譯的時(shí)候會(huì)報(bào)錯(cuò)?我的理解是,這應(yīng)該是一種語法錯(cuò)誤,而且是一種規(guī)范要求,ES6語法的規(guī)范要求,這種要求并不是說會(huì)影響到代碼的實(shí)際執(zhí)行。舉個(gè)栗子:

// typescript中一段簡單的繼承代碼實(shí)現(xiàn)
class Parent {
    name = "parent";
    func = function() {
        console.log("func in parent called.");
    }
}

class Child extends Parent {
    age = 3;
    func = function() {
        console.log("age is: ", this.age);    // 使用了this,不會(huì)報(bào)錯(cuò)
    }
}

這段代碼非常簡單,在子類中使用了this關(guān)鍵字,編譯時(shí)不會(huì)報(bào)錯(cuò),也可以正常執(zhí)行。然后我們進(jìn)行一點(diǎn)修改,在子類中引入constructor方法

class Child extends Parent {
    age = 3;
    // error TS2377: Constructors for derived classes must contain a "super" call.
    constructor() {
        
    }
    func = function() {
        console.log("age is: ", this.age);
    }
}

可以看到,編譯階段已經(jīng)開始報(bào)錯(cuò)了。在typescript的語法中,子類的constructor方法中不但需要調(diào)用super方法,而且必須在第一行代碼就調(diào)用super,否則都是會(huì)報(bào)錯(cuò)的??聪旅孢@段代碼:

class Child extends Parent {
    age = 3;
    constructor() {
        console.log("First line in constructor without super method");
        super();    // error TS2376: A "super" call must be the first statement in the constructor when a class contains initialized properties or has parameter properties.
    }
    func = function() {
        console.log("age is: ", this.age);
    }
}

來,我們接著改

class Parent {
    name = "parent";
    func = function() {
        console.log("func in parent called.");
    }
}

class Child extends Parent {
    age = 3;
    constructor() {
        console.log("Show property of parent, name is: ", this.name);    // error TS17009: "super" must be called before accessing "this" in the constructor of a derived class.
        console.log("Show property of child, age is: ", this.age);        // error TS17009: "super" must be called before accessing "this" in the constructor of a derived class.
        super();    // error TS2376: A "super" call must be the first statement in the constructor when a class contains initialized properties or has parameter properties.
        console.log("Show property of parent, name is: ", this.name);
        console.log("Show property of child, age is: ", this.age);
    }
    func = function() {
        console.log("age is: ", this.age);
    }
}

可以看到,編譯期已經(jīng)開始報(bào)各種錯(cuò)誤了,不過這不重要,我們這里利用typescript的編譯器(tsc)來進(jìn)行編譯,并查看編譯后的代碼內(nèi)容:

var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var Parent = (function () {
    function Parent() {
        this.name = "parent";
        this.func = function () {
            console.log("func in parent called.");
        };
    }
    return Parent;
}());
var Child = (function (_super) {
    __extends(Child, _super);
    function Child() {
        var _this = this;
        _this.age = 3;
        _this.func = function () {
            console.log("age is: ", this.age);
        };
        console.log("Show property of parent, name is: ", _this.name); // 輸出undefined,因?yàn)榇藭r(shí)子類的實(shí)例上還沒有繼承到父類的屬性值
        console.log("Show property of child, age is: ", _this.age); // 輸出3,子類實(shí)例自己的屬性值可以訪問
        _this = _super.call(this) || this; // 構(gòu)造函數(shù)式的繼承實(shí)現(xiàn),這一步就是講父類的屬性值設(shè)置到子類實(shí)例上
        console.log("Show property of parent, name is: ", _this.name); // 輸出parent,此時(shí)子類的實(shí)例上經(jīng)過上一步的繼承,得到了父類的屬性值
        console.log("Show property of child, age is: ", _this.age);  // 輸出3,子類實(shí)例自己的屬性值可以訪問
        return _this;
    }
    return Child;
}(Parent));
//# sourceMappingURL=demo.js.map

由此可以知道,在ES6中使用extends進(jìn)行繼承操作的過程中,

子類并非必須調(diào)用super方法,除非存在constructor方法

在constructor方法中應(yīng)該首先調(diào)用super方法,這是語法要求,不過這不是必須的

在調(diào)用super方法之前,將無法通過this關(guān)鍵字來訪問父類的屬性(這里就可以解釋其他文章中提到的 ‘若不執(zhí)行super,則this無法初始化’,更準(zhǔn)確的說法應(yīng)該是‘若不執(zhí)行super,則無法將父類的屬性值初始化到當(dāng)前子類實(shí)例上’)

子類中使用super.prop和super[expr]的方式是如何訪問父類的屬性和方法?

我們直接來看代碼吧,關(guān)鍵點(diǎn)都注釋了的

class Parent {
    public name = "parent";
    public static staticName = "staticParent";
    public static staticFunc() {
        console.log("staticFunc called in parent.");
    }

    public arrowFunc = () => {
        console.log("arrowFunc called in parent.");
    }

    public normalFunc() {
        console.log("normalFunc called in parent.")
    }
}

class Child extends Parent {
    public static staticFunc() {
        super.staticFunc();
        console.log("staticFunc called in Child.");
    }

    arrowFunc = () => {
        super.arrowFunc();
        console.log("arrowFunc called in Child.");
    }

    normalFunc() {
        super.normalFunc();
        console.log("normalFunc called in Child.")
    }

    getName() {
        console.log("parent name is: ", super.name);
        console.log("parent staticName is: ", super.staticName);
        console.log("child name is: ", this.name);
    }
}

/** 編譯后的代碼 **/
var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var Parent = (function () {
    function Parent() {
        this.name = "parent";
        this.arrowFunc = function () {
            console.log("arrowFunc called in parent.");
        };
    }
    // 編譯后的靜態(tài)方法可以存在于Parent類的內(nèi)部
    Parent.staticFunc = function () {
        console.log("staticFunc called in parent.");
    };
    Parent.prototype.normalFunc = function () {
        console.log("normalFunc called in parent.");
    };
    return Parent;
}());
Parent.staticName = "staticParent"; // 編譯后的靜態(tài)屬性依然存在于Parent類外
var Child = (function (_super) {
    __extends(Child, _super);
    function Child() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.arrowFunc = function () { // 子類實(shí)例調(diào)用arrowFunc的時(shí)候會(huì)報(bào)錯(cuò),因?yàn)開super.prototype上是不存在arrowFunc方法的
            _super.prototype.arrowFunc.call(_this); // Uncaught TypeError: Cannot read property "call" of undefined
            console.log("arrowFunc called in Child.");
        };
        return _this;
    }
    Child.staticFunc = function () {
        _super.staticFunc.call(this);   // super可以正常訪問父類的靜態(tài)方法
        console.log("staticFunc called in Child.");
    };
    Child.prototype.normalFunc = function () {
        _super.prototype.normalFunc.call(this);
        console.log("normalFunc called in Child.");
    };
    Child.prototype.getName = function () {
        console.log("parent name is: ", _super.prototype.name); // 輸出undefined, 父類原型(_super.prototype)上不存在name屬性
        console.log("parent staticName is: ", _super.prototype.staticName); // 輸出undefined,super無法正常訪問父類的靜態(tài)屬性
        console.log("child name is: ", this.name);  // 輸出parent,這是子類實(shí)例上的屬性,繼承自父類
    };
    return Child;
}(Parent));
//# sourceMappingURL=demo.js.map

這里再順嘴提一句,關(guān)于靜態(tài)屬性和靜態(tài)方法的區(qū)別。為什么在子類中通過super關(guān)鍵字來獲取父類的靜態(tài)方法經(jīng)過編譯后是_super.staticFunc,而獲取靜態(tài)屬性依然是_super.prototype.staticName,從原型上獲取導(dǎo)致獲取失敗呢?這個(gè)問題目前我還沒有找到答案,希望有知道的小伙伴可以不吝指教。
不過我倒是搜到一些其他相關(guān)內(nèi)容。
Class 的靜態(tài)屬性和實(shí)例屬性

因?yàn)?ES6 明確規(guī)定,Class 內(nèi)部只有靜態(tài)方法,沒有靜態(tài)屬性。

雖然這種規(guī)定從ES7開始得到了修正,我們目前已經(jīng)可以將靜態(tài)屬性寫在Class的內(nèi)部,但是經(jīng)過編譯之后可以發(fā)現(xiàn),靜態(tài)屬性依然存在于類的實(shí)現(xiàn)的外部。

var Parent = (function () {
    function Parent() {
        this.name = "parent";
        this.arrowFunc = function () {
            console.log("arrowFunc called in parent.");
        };
    }
    // 編譯后的靜態(tài)方法可以存在于Parent類的內(nèi)部
    Parent.staticFunc = function () {
        console.log("staticFunc called in parent.");
    };
    Parent.prototype.normalFunc = function () {
        console.log("normalFunc called in parent.");
    };
    return Parent;
}());
Parent.staticName = "staticParent"; // 編譯后的靜態(tài)屬性依然存在于Parent類外
回到問題本身

問:箭頭函數(shù)在繼承過程中無法通過super關(guān)鍵字獲取,這是為什么呢?
答:因?yàn)樽宇愔惺褂胹uper.prop和super[expr]的方式獲取的是父類原型(prototype)上的方法,靜態(tài)方法除外。

參考資料

從ES6中的extends講js原型鏈與繼承
React ES6 class constructor super()
Class 的靜態(tài)屬性和實(shí)例屬性

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/97061.html

相關(guān)文章

  • 從Vue數(shù)組響應(yīng)化所引發(fā)思考

    摘要:因?yàn)闊o法通過借用構(gòu)造函數(shù)的方式創(chuàng)建響應(yīng)式屬性雖然屬性可以被創(chuàng)建,但不具備響應(yīng)式功能,因此在我們是沒法繼承數(shù)組的。上面整個(gè)的文章都是基于監(jiān)聽數(shù)組響應(yīng)的一個(gè)點(diǎn)想到的。 前言   首先歡迎大家關(guān)注我的Github博客,也算是對我的一點(diǎn)鼓勵(lì),畢竟寫東西沒法獲得變現(xiàn),能堅(jiān)持下去也是靠的是自己的熱情和大家的鼓勵(lì)。   從上一篇文章響應(yīng)式數(shù)據(jù)與數(shù)據(jù)依賴基本原理開始,我就萌發(fā)了想要研究Vue源碼的想法...

    hikui 評論0 收藏0
  • 一道面試題引發(fā)思考

    摘要:下面我們來使用面向?qū)ο箢悎D這里就不再畫了首先面試題中所提到的我們都可以看成類,比如停車場是一個(gè)類吧,它里面的車位是一個(gè)類吧,攝像頭,屏幕。。。 以下是某場的一道面試題(大概): 1、一個(gè)停車場,車輛入場時(shí),攝像頭記錄下車輛信息2、屏幕上顯示所接收的車輛的信息情況(車牌號(hào))以及各層車位的車位余量3、停車場一共四層車位,其中的三層都為普通車位,還有一層為特殊車位(體現(xiàn)在停車計(jì)費(fèi)價(jià)格上面的不...

    Apollo 評論0 收藏0
  • 一道面試題引發(fā)思考

    摘要:下面我們來使用面向?qū)ο箢悎D這里就不再畫了首先面試題中所提到的我們都可以看成類,比如停車場是一個(gè)類吧,它里面的車位是一個(gè)類吧,攝像頭,屏幕。。。 以下是某場的一道面試題(大概): 1、一個(gè)停車場,車輛入場時(shí),攝像頭記錄下車輛信息2、屏幕上顯示所接收的車輛的信息情況(車牌號(hào))以及各層車位的車位余量3、停車場一共四層車位,其中的三層都為普通車位,還有一層為特殊車位(體現(xiàn)在停車計(jì)費(fèi)價(jià)格上面的不...

    soasme 評論0 收藏0
  • 一道面試題引發(fā)思考

    摘要:下面我們來使用面向?qū)ο箢悎D這里就不再畫了首先面試題中所提到的我們都可以看成類,比如停車場是一個(gè)類吧,它里面的車位是一個(gè)類吧,攝像頭,屏幕。。。 以下是某場的一道面試題(大概): 1、一個(gè)停車場,車輛入場時(shí),攝像頭記錄下車輛信息2、屏幕上顯示所接收的車輛的信息情況(車牌號(hào))以及各層車位的車位余量3、停車場一共四層車位,其中的三層都為普通車位,還有一層為特殊車位(體現(xiàn)在停車計(jì)費(fèi)價(jià)格上面的不...

    Backache 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<