總目錄
從C#到TypeScript - 類
在ES6之前Javascript的類都是用function定義的,ES6把類關鍵字正式加進來,雖說其實也還是function,不過代碼可讀性上好了不少。
TypeScript同樣支持class,並且和C#也非常相似,下面來看看:
類
同C#一樣,由構造函數,屬性,方法組成,屬性和方法有三個級別的訪問權限:private, protected, public,比C#少個internal。
不過不同的是C#類的成員默認是private,而TypeScript默認是public。
在類里面所有成員都必須用this來訪問。
class User{
constructor(name: string, pwd: string){
this.name = name;
this.pwd = pwd;
}
name: string;
private pwd: string;
checkLogin(): boolean{
return this.name === 'brook' && this.pwd === '123';
}
}
let u: User = new User('brook', '123');
console.info(u.checkLogin()); // true
u.name = 'test';
console.info(u.checkLogin()); // false
參數屬性
上面的User類有兩個成員,而且都是從構造函數賦值的,也就是其實構造函數的參數就是類的成員,這就是參數屬性。
類里面的那兩個屬性其實可以不用寫,只要在構造函數的參數上加上操作限定符,TypeScript就會自動為參數生成屬性,來重構下上面的User。
class User{
constructor(public name: string, private pwd: string){ }
checkLogin(): boolean{
return this.name === 'brook' && this.pwd === '123';
}
}
getter/setter
同樣,也有存取器,不過語法和C#不太一樣,寫起來稍麻煩些。
只有get的時候也就變成只讀屬性了。
class User{
private _name: string;
get name(): string{
return this._name;
}
set name(name: string){
this._name = name;
}
}
靜態屬性和方法
上面說的都是實例成員,TypeScript也支持靜態成員,不用實例化,而是通過類名來訪問。
class User{
static permission = 'user';
static setPermission(p: string){
User.permission = p;
}
}
console.info(User.permission); // user
User.setPermission('admin');
console.info(User.permission); // admin
也同時支持static和readonly,不過static要放在前面,這樣可以實現單例模式。
class User{
static readonly instance = new User();
private constructor(){}
checkLogin(name: string, pwd: string): boolean{
return name === 'brook' && pwd === '123';
}
}
console.info(User.instance.checkLogin('brook', '123'));
抽象類
這點和C#一樣,都可以用抽象類來把有共同行為抽象出來,關鍵字都是abstract。
不能實例化,可以包含實現,abstract標識的方法,繼承類必須實現。
但沒有virtual關鍵字,不過和Java一樣,可以認為是天生虛函數,也不需要override,直接覆蓋也能支持多態。
繼承類里要調用父類的函數需要用super關鍵字。
abstract class User{
name: string;
pwd: string;
abstract checkLogin(): boolean;
checkName(): boolean {
return this.name.indexOf('.') < 0;
}
}
class Admin extends User {
checkLogin(): boolean { // 必須實現的抽象方法
return this.checkName();
}
checkName(): boolean { // 這里會把User里的checkName覆蓋掉
return true && super.checkName();
}
}
let user: User;
user = new Admin();
user.name = 'brook.shi';
console.info(user.checkLogin()); // 同樣有多態,checkLogin里調用的是Admin的checkName
另外,繼承時還需要注意,如果派生類里有構造函數,則構造函數必須要調用父類的構造函數:super()。
兼容性
TypeScript里的類是有兼容性的,這點和C#很不一樣,TypeScript認為:只有成員的類型是兼容的,那它們的類型也是兼容的。
不過成員的兼容對於public很寬容,完全不相干的類如果全是public的成員,並且一致就認為是兼容的。
但對於private和protected則只有是繼承的才會被認可為兼容。
class Test1{
name: string;
pwd: string;
checkName(): boolean{
return true;
}
}
class Test2{
name: string;
pwd: string;
checkName(): boolean{
return false;
}
}
let t: Test1 = new Test2();
console.info(t.checkName()); // false
如果給上面的Test和Test2各加一個private email: string;,結果是編譯不了,因為它們並不是繼承關系,也就兼容不了。
泛型
同接口一樣支持泛型,用法也一樣,可以參考接口泛型。
interface Testable<T> {
field: T;
method(arg: T): T;
}
class Test<T> implements Testable<T>{
field: T;
method(arg: T): T{
console.info(`arg is ${typeof arg}`);
return null;
}
}
let test11 = new Test<string>();
test11.method('method'); // arg is string
test11.method(123); // error, 123 is not string
總的來說,TypeScript的類和C#或Java可以說十分相似,除了兼容性基本上沒有什么新的東西,不過對於JavaScript來說卻是一大進步了。
