摘要:舉個(gè)栗子中一段簡(jiǎn)單的繼承代碼實(shí)現(xiàn)使用了,不會(huì)報(bào)錯(cuò)這段代碼非常簡(jiǎn)單,在子類(lèi)中使用了關(guān)鍵字,編譯時(shí)不會(huì)報(bào)錯(cuò),也可以正常執(zhí)行。參考資料從中的講原型鏈與繼承的靜態(tài)屬性和實(shí)例屬性
問(wèn)題引入
最近一直在看原型繼承相關(guān)的東西,翻到這么一篇文章: 從ES6中的extends講js原型鏈與繼承
文中有一個(gè)點(diǎn)讓我很感興趣,箭頭函數(shù)在繼承過(guò)程中無(wú)法通過(guò)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):
子類(lèi)中存在constructor方法的時(shí)候,需要調(diào)用super方法,并且需要在使用this關(guān)鍵字之前調(diào)用
super關(guān)鍵字可以用來(lái)調(diào)用父對(duì)象上的方法
可以使用super來(lái)調(diào)用父對(duì)象上的靜態(tài)方法
不可以使用delete來(lái)刪除super上的屬性
不可以復(fù)寫(xiě)super對(duì)象上的只讀屬性
子類(lèi)中是否必須主動(dòng)調(diào)用super方法?我的看法是不需要。
網(wǎng)上有些文章(比如這篇)寫(xiě)道:
因?yàn)槿舨粓?zhí)行super,則this無(wú)法初始化。
我的個(gè)人理解是,this是指代執(zhí)行上下文環(huán)境的,不存在無(wú)法初始化的情況。更準(zhǔn)確的說(shuō)法是這樣:如果不使用super方法,那么父類(lèi)中的屬性值無(wú)法進(jìn)行初始化,如果這個(gè)時(shí)候子類(lèi)通過(guò)this字段來(lái)訪問(wèn)了父類(lèi)中的屬性值,那么只能得到一個(gè)undefined。至于為什么這么寫(xiě)編譯的時(shí)候會(huì)報(bào)錯(cuò)?我的理解是,這應(yīng)該是一種語(yǔ)法錯(cuò)誤,而且是一種規(guī)范要求,ES6語(yǔ)法的規(guī)范要求,這種要求并不是說(shuō)會(huì)影響到代碼的實(shí)際執(zhí)行。舉個(gè)栗子:
// typescript中一段簡(jiǎn)單的繼承代碼實(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ò) } }
這段代碼非常簡(jiǎn)單,在子類(lèi)中使用了this關(guān)鍵字,編譯時(shí)不會(huì)報(bào)錯(cuò),也可以正常執(zhí)行。然后我們進(jìn)行一點(diǎn)修改,在子類(lèi)中引入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)開(kāi)始報(bào)錯(cuò)了。在typescript的語(yǔ)法中,子類(lèi)的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); } }
來(lái),我們接著改
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)開(kāi)始報(bào)各種錯(cuò)誤了,不過(guò)這不重要,我們這里利用typescript的編譯器(tsc)來(lái)進(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í)子類(lèi)的實(shí)例上還沒(méi)有繼承到父類(lèi)的屬性值 console.log("Show property of child, age is: ", _this.age); // 輸出3,子類(lèi)實(shí)例自己的屬性值可以訪問(wèn) _this = _super.call(this) || this; // 構(gòu)造函數(shù)式的繼承實(shí)現(xiàn),這一步就是講父類(lèi)的屬性值設(shè)置到子類(lèi)實(shí)例上 console.log("Show property of parent, name is: ", _this.name); // 輸出parent,此時(shí)子類(lèi)的實(shí)例上經(jīng)過(guò)上一步的繼承,得到了父類(lèi)的屬性值 console.log("Show property of child, age is: ", _this.age); // 輸出3,子類(lèi)實(shí)例自己的屬性值可以訪問(wèn) return _this; } return Child; }(Parent)); //# sourceMappingURL=demo.js.map
由此可以知道,在ES6中使用extends進(jìn)行繼承操作的過(guò)程中,
子類(lèi)并非必須調(diào)用super方法,除非存在constructor方法
在constructor方法中應(yīng)該首先調(diào)用super方法,這是語(yǔ)法要求,不過(guò)這不是必須的
在調(diào)用super方法之前,將無(wú)法通過(guò)this關(guān)鍵字來(lái)訪問(wèn)父類(lèi)的屬性(這里就可以解釋其他文章中提到的 ‘若不執(zhí)行super,則this無(wú)法初始化’,更準(zhǔn)確的說(shuō)法應(yīng)該是‘若不執(zhí)行super,則無(wú)法將父類(lèi)的屬性值初始化到當(dāng)前子類(lèi)實(shí)例上’)
子類(lèi)中使用super.prop和super[expr]的方式是如何訪問(wèn)父類(lèi)的屬性和方法?我們直接來(lái)看代碼吧,關(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類(lèi)的內(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類(lèi)外 var Child = (function (_super) { __extends(Child, _super); function Child() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.arrowFunc = function () { // 子類(lèi)實(shí)例調(diào)用arrowFunc的時(shí)候會(huì)報(bào)錯(cuò),因?yàn)開(kāi)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可以正常訪問(wèn)父類(lèi)的靜態(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, 父類(lèi)原型(_super.prototype)上不存在name屬性 console.log("parent staticName is: ", _super.prototype.staticName); // 輸出undefined,super無(wú)法正常訪問(wèn)父類(lèi)的靜態(tài)屬性 console.log("child name is: ", this.name); // 輸出parent,這是子類(lèi)實(shí)例上的屬性,繼承自父類(lèi) }; return Child; }(Parent)); //# sourceMappingURL=demo.js.map
這里再順嘴提一句,關(guān)于靜態(tài)屬性和靜態(tài)方法的區(qū)別。為什么在子類(lèi)中通過(guò)super關(guān)鍵字來(lái)獲取父類(lèi)的靜態(tài)方法經(jīng)過(guò)編譯后是_super.staticFunc,而獲取靜態(tài)屬性依然是_super.prototype.staticName,從原型上獲取導(dǎo)致獲取失敗呢?這個(gè)問(wèn)題目前我還沒(méi)有找到答案,希望有知道的小伙伴可以不吝指教。
不過(guò)我倒是搜到一些其他相關(guān)內(nèi)容。
Class 的靜態(tài)屬性和實(shí)例屬性
因?yàn)?ES6 明確規(guī)定,Class 內(nèi)部只有靜態(tài)方法,沒(méi)有靜態(tài)屬性。
雖然這種規(guī)定從ES7開(kāi)始得到了修正,我們目前已經(jīng)可以將靜態(tài)屬性寫(xiě)在Class的內(nèi)部,但是經(jīng)過(guò)編譯之后可以發(fā)現(xiàn),靜態(tài)屬性依然存在于類(lèi)的實(shí)現(xiàn)的外部。
var Parent = (function () { function Parent() { this.name = "parent"; this.arrowFunc = function () { console.log("arrowFunc called in parent."); }; } // 編譯后的靜態(tài)方法可以存在于Parent類(lèi)的內(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類(lèi)外回到問(wèn)題本身
問(wèn):箭頭函數(shù)在繼承過(guò)程中無(wú)法通過(guò)super關(guān)鍵字獲取,這是為什么呢?
答:因?yàn)樽宇?lèi)中使用super.prop和super[expr]的方式獲取的是父類(lèi)原型(prototype)上的方法,靜態(tài)方法除外。
從ES6中的extends講js原型鏈與繼承
React ES6 class constructor super()
Class 的靜態(tài)屬性和實(shí)例屬性
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/97061.html
摘要:因?yàn)闊o(wú)法通過(guò)借用構(gòu)造函數(shù)的方式創(chuàng)建響應(yīng)式屬性雖然屬性可以被創(chuàng)建,但不具備響應(yīng)式功能,因此在我們是沒(méi)法繼承數(shù)組的。上面整個(gè)的文章都是基于監(jiān)聽(tīng)數(shù)組響應(yīng)的一個(gè)點(diǎn)想到的。 前言 首先歡迎大家關(guān)注我的Github博客,也算是對(duì)我的一點(diǎn)鼓勵(lì),畢竟寫(xiě)東西沒(méi)法獲得變現(xiàn),能堅(jiān)持下去也是靠的是自己的熱情和大家的鼓勵(lì)。 從上一篇文章響應(yīng)式數(shù)據(jù)與數(shù)據(jù)依賴(lài)基本原理開(kāi)始,我就萌發(fā)了想要研究Vue源碼的想法...
摘要:下面我們來(lái)使用面向?qū)ο箢?lèi)圖這里就不再畫(huà)了首先面試題中所提到的我們都可以看成類(lèi),比如停車(chē)場(chǎng)是一個(gè)類(lèi)吧,它里面的車(chē)位是一個(gè)類(lèi)吧,攝像頭,屏幕。。。 以下是某場(chǎng)的一道面試題(大概): 1、一個(gè)停車(chē)場(chǎng),車(chē)輛入場(chǎng)時(shí),攝像頭記錄下車(chē)輛信息2、屏幕上顯示所接收的車(chē)輛的信息情況(車(chē)牌號(hào))以及各層車(chē)位的車(chē)位余量3、停車(chē)場(chǎng)一共四層車(chē)位,其中的三層都為普通車(chē)位,還有一層為特殊車(chē)位(體現(xiàn)在停車(chē)計(jì)費(fèi)價(jià)格上面的不...
摘要:下面我們來(lái)使用面向?qū)ο箢?lèi)圖這里就不再畫(huà)了首先面試題中所提到的我們都可以看成類(lèi),比如停車(chē)場(chǎng)是一個(gè)類(lèi)吧,它里面的車(chē)位是一個(gè)類(lèi)吧,攝像頭,屏幕。。。 以下是某場(chǎng)的一道面試題(大概): 1、一個(gè)停車(chē)場(chǎng),車(chē)輛入場(chǎng)時(shí),攝像頭記錄下車(chē)輛信息2、屏幕上顯示所接收的車(chē)輛的信息情況(車(chē)牌號(hào))以及各層車(chē)位的車(chē)位余量3、停車(chē)場(chǎng)一共四層車(chē)位,其中的三層都為普通車(chē)位,還有一層為特殊車(chē)位(體現(xiàn)在停車(chē)計(jì)費(fèi)價(jià)格上面的不...
摘要:下面我們來(lái)使用面向?qū)ο箢?lèi)圖這里就不再畫(huà)了首先面試題中所提到的我們都可以看成類(lèi),比如停車(chē)場(chǎng)是一個(gè)類(lèi)吧,它里面的車(chē)位是一個(gè)類(lèi)吧,攝像頭,屏幕。。。 以下是某場(chǎng)的一道面試題(大概): 1、一個(gè)停車(chē)場(chǎng),車(chē)輛入場(chǎng)時(shí),攝像頭記錄下車(chē)輛信息2、屏幕上顯示所接收的車(chē)輛的信息情況(車(chē)牌號(hào))以及各層車(chē)位的車(chē)位余量3、停車(chē)場(chǎng)一共四層車(chē)位,其中的三層都為普通車(chē)位,還有一層為特殊車(chē)位(體現(xiàn)在停車(chē)計(jì)費(fèi)價(jià)格上面的不...
閱讀 1999·2021-11-19 09:40
閱讀 1954·2021-09-28 09:36
閱讀 2291·2021-09-22 10:02
閱讀 2731·2019-08-30 14:00
閱讀 1956·2019-08-29 15:31
閱讀 2904·2019-08-29 15:11
閱讀 2913·2019-08-29 13:04
閱讀 1086·2019-08-27 10:55