[ESNext]現在就學起來:class

ES5 及之前

定義

在 ES5 中,並沒有類的概念,但是已經有了類型的概念。

那時,我們想創建一個類型,是這樣做的:

function MyClass() {
   alert(this instanceof MyClass);
}

如此,我們定義了一個類型,但同時,他也是一個函數。

我們看看不同調用方法所得到的結果:

MyClass(); // false
new MyClass(); // true

第一句直接調用會輸出 false ,因爲函數內上下文 (context) 會指向 window ,因此會去檢測 window 的 prototype 是否存在 MyClass 的原型(即,是否從 MyClass 繼承而來)。答案是顯而易見的。

而使用 new 去創建一個對象並鏈接到 MyClass 的時候,new 出來的對象 prototype 是存在 MyClass 的原型的。而且在執行時他的上下文會指向用 new 創建出的這個對象,所以 this 指向 (new MyClass 創建出的對象) instanceof MyClass 則是成立的。

 

繼承

上文中我們提到了繼承,但是在沒有類的情況下如何進行繼承呢?

這個時候會有人想到,既然有原型的概念,那麼是不是可以在原型上去累加原型形成一個原型鏈來實現這個效果?

於是,出現了這樣一種寫法:

function A() {
    this.text = "father";
}

A.prototype.Print = function () {
    alert(this.text);
};

function B() {
    this.text = "son";
}

B.prototype = new A();
B.prototype.constructor = B;

var a = new A();
a.Print();

var b = new B();
b.Print();

初看是不是一頭霧水?誰是父類?誰是子類? this.text 到底指向誰?B.prototype = new A(); 是什麼鬼?直接 = A 爲什麼不行?

B.prototype.constructor 爲什麼要等於 B?不等於不行嗎?等等等等問題~

我們一步一步解析看看~

首先,A 是父類,B是子類。

this.text 主要看 new 出來的對象指向的是由 A 創建的還是 B 創建的。

B.prototype 如果直接等於 A ,那麼如果修改 B.prototype 下的內容,就相當於直接修改了 A 的內容,而我們其實想修改的是 A 創建出來的副本對象。

B.prototype.constructor 默認指向 B 的構造器,但是由於 B.prototype 被修改,所以需要重新指向,否則會造成構造器相關操作錯誤的指向 A 進而出錯。

哇~看到這裏你發現,怎麼這麼麻煩啊,我只是單純的想讓 B 繼承一下 A 的東西而已啊。

沒錯,就是這麼一個簡單的事情在 ES5 及之前並做不到很簡單的去寫和去理解。

 

私有函數、私有變量

這兩個真的做不到,沒辦法。不論是從語法、解析、可能的寫法,都做不到。

什麼?你說在函數和變量前加一個 _ 下劃線代表私有?

那是不是意味着我可以隨意對你的庫進行注入修改邏輯或者截取祕密數據?

 

 ES6 時代

定義

在 ES6 時代,已經有了 class 關鍵字及其概念。

這時,想定義一個類型就非常簡單了:

class MyClass {
    constructor() {
        console.dir(this);
    }
}

注意:constructor 不是必須的,只有當你在 new 的時候想傳入一些參數或者做一些事的時候才需要。這裏爲了演示。

爲什麼這個演示代碼中我不再寫 this instanceof MyClass 了呢?

因爲這個時候我們直接調用 MyClass() 已經會報錯了。這種寫法在檢查上已經和函數有了一定的區別。

Uncaught TypeError: Class constructor MyClass cannot be invoked without 'new',

但是請注意, typeof MyClass 依然輸出的是 function 。

雖然如此,給我們帶來的好處還是很多的,上面這條算一個。

另外,this 上下文已經不會因爲誤調用而產生指向問題。這裏特別指出,我着重了“誤調用”,因爲你如果使用 .call 或者 .apply 調用對象內函數強制改變上下文還是可以做到的。

 

繼承

既然定義有了改變,那麼繼承方式是否有改變呢?答案是肯定的,這次改動隱藏了絕大部分的概念和複雜邏輯。

寫法如下:

class A {
    constructor() {
        this.text = "father";
    }

    Print() {
        alert(`father's ${this.text}`);
    }
}

class B extends A {
    constructor() {
        super();

        this.text = "son";
    }

    Print() {
        alert(`son's ${this.text}`);
    }

    PrintA() {
        super.Print();
    }
}

const a = new A();
a.Print();

const b = new B();
b.Print();
b.PrintA();

在此例子中,我們定義了一個 class A,賦值了一個 text 爲 father ,然後定義了一個函數 Print 。在這裏爲了區別子類,特意加上了 father's 前綴進行輸出。

然後定義了一個 class B,並賦值了 text 爲 son,注意!constructor 的第一行必須是 super(); 也就是說,子類必須在構造自身前先對父類進行構造,否則在操作 this 時會報錯。

我們同時對 class B 中定義一個函數 Print 和 PrintA。

然後對 A 和 B 分別用 new 運算符創建對象。

調用 a.Print() 輸出 father's father

調用 b.Print() 輸出 son's son

從開始到現在我們都沒有問題,需要留意的是下一句:

b.PrintA()

之類需要注意一下,我們內部是怎麼寫的呢?

super.Print();

也就是說,我們調用了繼承的父類的 Print 函數,此時輸出 father's son

在這個例子中,我們沒有因爲語法寫法而感到迷惑,也不會造成,哦天啊!他們兩個混爲一談了這樣的問題。

可以說是十分簡潔了。

最後需要注意一下! ES6 的 class 語法只支持單繼承,也就是說, B 不能同時繼承 A、C、....等等更多的類,該語法只支持繼承一個。當然,有其他方式可以解決,這是後話了。

 

私有函數、私有變量

雖然在語法上有了革新,但是因底層限制,依然沒有私有函數於私有變量。

 

EXNext

從 ES6 開始,EcmaScript 不再以版本號作爲升級標識而是改成了年份標識。

也就是說,一年會升級一次或更多次。我們統稱其爲 ESNext。

定義

class A {
    #text = "nivk";

    age = 52;

    get Text() {
        return this.#text;
    }

    Print() {
        alert(`我叫 ${this.Text},我今年 ${this.age} 歲。`);
    }
}

class B extends A {
    #text = "dog";

    age = 26;
}

const a = new A();
a.Print();

const b = new B();
b.Print();

我們定義了 A ,並寫了一個私有變量 #text ,寫了一個公共變量 age,一個 get 訪問器 Text 還有一個函數 Print。

再定義一個 B,覆寫了 #text 私有變量和公共變量 age。

輸出結果令人驚奇:

我叫 nivk,我今年 52 歲。

我叫 nivk,我今年 26 歲。

畢竟,我不是 dog 不是麼~(題外話)

爲什麼 age 被覆寫了而 #text 沒有呢?這裏並不是這個 get 訪問器起了作用,而是 #text 本身被定義成了私有的,那麼他的可訪問性就受到了限制,#text 只能被自身讀寫,而不能被子類、外部任何代碼中訪問和寫入。

私有屬性或私有函數使用前綴 # 井號來標識,爲何使用此符號標識呢?這涉及到了一些性能問題,如有興趣請至 TC39 委員會的 Github 倉庫查閱。

這就是私有變量。同樣我們也可以在 class 中定義私有變量如下所示(僅關鍵代碼):

#TestFunction() {
    // balabala....
}

Print() {
    this.#TestFunction();
}

如此一來,我們便可安心將類庫發佈出去也不怕別人誤用或可規避一些隱性安全風險。

 

結尾

EcmaScript 標準一直在更新,所以此文僅代表發表此文時的最新狀態,如果後續有語法、功能的更新則我也會在這裏進行更新。

素質二連!!~~~關注、點贊~~~撒花!~

 

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