ES6 / TypeScript / Babel / C# 中的 super(base)

今天看到 @justjavac 寫的《ES6 中的 this & super:babel 和 typescript 都錯了》,覺得很有意思,所以也研究了一下。

借用 @justjavac 的示例代碼,略做修改,然後在幾種語言中跑了一下,結果

語言(版本) 輸出1 輸出2 輸出3
ES6 3 undefined 3
Babel 2 undefined 2
TypeScript (?) 2 3 2
C# 3 3 3
Java 3 3 3

是的,我加入了 C# 和 Java 的運行結果,畢竟它們是真正的 OOP 語言。另外請注意到,我在 TypeScript 後面加了個問號 (?),因爲實際上 TypeScript 雖然編譯成了對應的 JS,但是轉譯過程中是會報錯的:

index.ts (20,15): Only public and protected methods of the base class are accessible via the 'super' keyword. (2340)
index.ts (22,27): Only public and protected methods of the base class are accessible via the 'super' keyword. (2340)

下面,我從 C#/Java 說起

C# / Java

對於 C#/Java 這樣的真正的 OOP 語言來說,super.xthis.x 其實是一個東西,因爲在子類中沒有重新定義這個成員 x。以 C# 代碼爲例

using System;

public class Program
{
   public static void Main()    {
       var t = new ColorPoint();        t.test();    } }

class Point {
   public int x;
   protected void getValue() {        Console.WriteLine(this.x);    } }

class ColorPoint : Point
{
   public ColorPoint() {
       this.x = 2;
       base.x = 3;        Console.WriteLine(this.x);        Console.WriteLine(base.x);    }

   public void test() {
       this.getValue();    } }

上面這段代碼是爲了與下面這段代碼進行比較——假如我們在子類中重新定義 x 呢?

class ColorPoint : Point {

   public new int x;

   public ColorPoint() {
       this.x = 2;
       base.x = 3;        Console.WriteLine(this.x);        Console.WriteLine(base.x);    }

   public void test() {
       this.getValue();    } }

它的輸出是 233,爲什麼?

this.x2 好理解,super.x3 也好理解。而 getValue() 中實際取的是父類中的 x,似乎有點不好理解——

其實也不難理解,因爲子類中重新定義了 x,它和中的 x 就不是同一個東西了,只是正好名稱相同而已。

從另一個方面來理解:子類中重新定義 x,而不是重載(也不可能重載字段,只有方法和屬性可以重載),那麼 getValue() 就不會順着虛函數鏈去找到最近的一個定義,也就不會取到子類中的賦值。

TypeScript

在 TypeScript 的 Playground 中運行下面的代碼確實可以得到 232

class Point {
   public x: number;    protected getValue() {
       console.log(this.x);    } }

class ColorPoint extends Point {
   constructor() {
       super();
       this.x = 2;
       super.x = 3;
       console.log(this.x);
       console.log(super.x);    }    test() {
       this.getValue();    } }

const t = new ColorPoint(); t.test();

問題在於,不管是在 Playground 還是 VSCode 還是 vsc(編譯器),都會得到錯誤提示

Only public and protected methods of the base class are accessible via the ‘super’ keyword.

這裏提到了用 super 的兩個條件,一個是 publicprotected 修飾,二個是 methods。第二個條件就是關鍵所在:TypeScript 中只能通過 super 調用方法,所以 super.x 從語法上來說就是錯的!我不知道 Anders Hejlsberg 爲什麼要在語法錯誤的情況仍然輸出結果——也許是爲了容錯性。但既然用 TypeScript,就是爲了用它的靜態檢查,所以要充值關注編譯錯誤提示。

現在來試驗一下介於 field 和 method 之間的情況,使用 getter/setter 語法的屬性。

class Point {
   private _x: number;

   public get x(): number {
       return this._x;    }

   public set x(value: number) {
       this._x = value;    }    protected getValue() {
       console.log(this.x);    } }

很遺憾,同樣的錯誤。就這一點來說,我覺得 TypeScript 還有待進步。

ES6 / ES2015 / Babel

ES6 的正式名稱是 ECMAScrip 2015,即 ES2015

那麼,現在來說說 ES6 的結果 3undefined3

……

可是,我除了說不能理解之外,還能說什麼呢?

既然 super.x = 3 都可以起作用,憑什麼 console.log(super.x) 就取不到值?從這一點上來說,Babel 的結果 (2undefined2) 反而更符合邏輯。

小結

ES6 的結果我不能理解,也許能從 ECMAScript 2015 Language Specification 中找到答案,不過我沒耐心去閱讀這個長而枯燥的英文文檔——如果有人找到了答案,麻煩告訴我一聲,萬分感謝!

不管怎麼說,我用 TypeScript 的時間比較多,而且忠實於編譯器的錯誤提示。因此在我實際工作中遇到類似問題的概率非常低,不糾結 ^_^

本文分享自微信公衆號 - 邊城客棧(fancyidea-full)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章