在此以前,經過《JavaScript => TypeScript 入門》已經掌握了類型聲明的寫法。原覺得憑着那一條無往不利的規則,就能夠開開心心的重寫 JS 項目了。當我躍躍欲試去重寫一個 JS 項目時,發現阻礙重重。編程
在投靠 TS 時,TypeScript 容許將 JS 漸進式的過渡到 TS:segmentfault
一些簡單的基本類型聲明的填充,好比:number
, :string
, :string[]
等等;安全
將一些稍複雜的、暫時還未提煉好的結構聲明爲 any
類型;數據結構
改後綴爲 .ts
, 執行 tsc
編譯。編程語言
但一個明顯的問題是,一個 JS 文件中每每有不少模塊依賴,意味着要把全部這些模塊都從新作一次類型聲明,才能總體編譯經過。函數
因此,要想暢通無阻的重寫 JS,在上一篇入門的基礎上,還得強忍着衝動,繼續學習兩部份內容:工具
一、TS 類的寫法、特徵
二、TS 模塊、命名空間學習
好在,它們和 ES6 其實有着不少重合的地方——要記得,TS 是 ES6 的超集。因此若是掌握了 ES6,咱們只要挑差別,找增量,進行遷移學習,就能快速掌握上述內容。this
ES6 的類的概念,和廣泛編程語言的類概念一致,表示一種特有的數據結構。它既像一個函數(能被 new
調用),又像一個對象(包含了各類屬性、方法)。code
class MyClass { constructor() { this.attr = 'my attribute'; } foo(name) { console.log('hello ' + name); } } let myclass = new MyClass();
類中的方法屬性被稱爲 「成員」,這些成員大概被分紅 3 種類別:
公有屬性、公有方法
私有屬性、私有方法
靜態屬性、靜態方法
在寫繼承時,咱們並不但願在全部時候,這些屬性、方法都被繼承到子類;在訪問成員時,也不但願在任什麼時候候,類實例的全部成員都無一例外能夠被訪問;有時候咱們但願與此相反,這樣能保持開放出去的信息簡潔乾淨。
JS 很早就有這些概念,但到如今爲止,ES6 並無處理好這個事情。
ES6 沒有直觀的表示私有屬性、私有方法的方案,只能經過變通的方式間接地表示。
const getKeys = Symbol('getKeys_'); const attr = Symbol('attr_'); class MyClass { constructor() { // 私有屬性 this[attr] = 'private attribute'; } // 公有方法 foo(config) { return this[fn](config); } // 私有方法 [getKeys](config) { return Object.keys(config); } };
這僅僅是一個間接取巧的方式避免直接被訪問到,但很輕易就能繞過它:
let myclass = new MyClass(); let symbolsAttr = Object.getOwnPropertySymbols(myclass); let attr_ = myclass[symbolsAttr[0]]; // 即訪問到私有屬性 let symbolsFn = Object.getOwnPropertySymbols(myclass.__proto__); let getKeys_ = myclass[symbolsFn[0]]; // 即訪問到私有方法
私有方法、私有屬性的目的是不想開放過多的信息到外部。上面變通的方案雖然表面上達到了目的,可是不夠直觀,也不安全。
ES6 目前支持靜態方法表示,類屬性及靜態屬性目前做爲提案還未正式成爲標準。
class MyClass { name = 'jeremy'; // ES6 不被支持,僅做爲提案 static version = '1.0.0'; // ES6 不被支持,僅做爲提案 constructor() { console.log(MyClass.version); // '1.0.0' } static get Version() { return MyClass.version; } }
區分這些成員身份,與動態類型、靜態類型語言沒有必然聯繫,能夠預見,在不久的未來,ES6 這方面將獲得完善。
鑑於 TS 是 ES6 的超集這一事實,TS 類固然也有私有屬性、私有方法
,靜態屬性、靜態方法
等這些身份的成員。在標記成員身份上,TS 與 Java 有不少類似之處。好比與繼承相關的訪問修飾符
,以及其餘限定做用的非訪問修飾符
。
訪問修飾符
private
public
protected
非訪問修飾符
static
readonly
abstract
class User { readonly name: string; public age: number; private sex: string; protected marriage: string; constructor(name: string, sex: string) { this.name = name; this.sex = sex; } showAge() { console.log(`${this.name}, age ${this.age}`); } }
一、當成員被標記成 private
時,它就不能在聲明它的類的外部訪問。
二、protected
修飾符與 private
修飾符的行爲很類似,但有一點不一樣,protected
成員在派生類中仍然能夠訪問
三、被聲明爲 public
的類、方法、構造方法和接口可以被任何其餘類訪問。
class User { readonly name: string; public age: number; private sex: string; protected marriage: string; constructor(name: string, sex: string) { this.name = name; this.sex = sex; } showAge() { console.log(this.age); } } let a = new User('jerry', 'male'); console.log(a.sex); // 編譯報錯:私有屬性不準在本類以外被訪問 console.log(a.marriage); // 編譯報錯:私有屬性不準在類以外被訪問 a.name = 'jeremy'; // 編譯報錯:只讀屬性不準再次被寫入
訪問修飾符主要用在繼承的可訪問性上,繼承比如遺傳,用基因類比理解它們就頗有意思:
private
私有基因——本體有效,不會被繼承,即子類中沒法訪問到
protected
被保護基因——保護血統純正,只容許在繼承體系中訪問
public
公共基因——子類、其餘類都能訪問到
有了訪問修飾符,每一個成員都有扮演着與身份對應的角色,真正作到名副其實。
訪問修飾符和非訪問修飾符加起來有6個甚至更多,它們如何與類、接口一塊兒搭配工做,考慮到組合狀況很是多,此處不去細緻的探究。能夠參考 Java 的規則:
default
(即缺省,什麼也不寫): 在同一包(等同於JS中的模塊)內可見,不使用任何修飾符。使用對象:類、接口、變量、方法。
private
: 在同一類內可見。使用對象:變量、方法。 注意:不能修飾類(外部類)
public
: 對全部類可見。使用對象:類、接口、變量、方法
protected
: 對同一包內的類和全部子類可見。使用對象:變量、方法。 注意:不能修飾類(外部類)。
固然更直觀的方式是上代碼 + 編譯。TS 的編譯工具備着很是友好、明確編譯錯誤提示。
class Demo { static private showSelf() {} } // 編譯錯誤提示 error TS1029: 'private' modifier must precede 'static' modifier.
固然,去查閱官方文檔也是一個好辦法。
非訪問修飾符不關心可訪問性,被它單獨標記的成員,在任什麼時候候都能訪問到。目前至少有這些:
static
get
set
readonly
abstract
從字面上看,不難理解它們的用途。這裏和 ES6 差異較大的是 abstract
。
abstract
修飾符用來定義抽象類和在抽象類內部定義抽象方法。
在 Java 中,抽象類不能用來實例化對象,主要作爲其它派生類的基類使用。 不一樣於接口,抽象類能夠包含成員的實現細節。
TS 中也是這樣規定的:抽象類不容許直接被實例化。
還有一點很重要,抽象類中的抽象方法能夠不包含具體實現,但必須在派生類中實現。
// 抽象類 abstract class User { readonly name: string; public age: number; private sex: string; protected marriage: string; constructor(name: string, sex: string) { this.name = name; this.sex = sex; } showAge() { console.log(this.age); } // 抽象方法 abstract showName(): void; } // let a = new User('jerry', 'male'); // 編譯報錯: 抽象類不容許直接實例化
容易忽略的一個問題是,一個類中一旦出現抽象方法,那這個類整個應該被標記爲 abstract
。
class UserA extends User { constructor(name: string, sex: string) { super(name, sex); // console.log(this.sex); // 編譯報錯:私有變量不能被訪問 } // 抽象方法必需要在子類中實現 // 若無此方法,編譯報錯 showName() { console.log(this.name); } }
基本上,對 TS 類只須要掌握這些,深刻的部分天然在實踐中繼續領悟。
對了,還有模塊、命名空間沒有梳理。由於碼文字的麻煩,只得留到後續再補。可是直接搬用 ES6 的模塊import
, export
就徹底夠用了。
So,這回真的去重寫了。