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