https://blog.csdn.net/qq_28506819/article/details/88615912
類(Class)
傳統的js使用函數和基於原型的方式來構建可重複使用的組件,es6開始可以使用基於類的繼承方式。Typescript允許開發者使用基於類的繼承方式,而不必等待新的JavaScript版本。
1、構建第一個類
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
這些語法看起來有點像java或C#,我們使用關鍵字class聲明瞭一個新類Greeter,然後使用new關鍵字創建了該類的一個實例。
2、繼承
在typescript中使用基於面向對象的模式,一個類可以繼承另一個存在的類,從而實現組件的複用。
class Animal {
move(distanceInMeters: number = 0) {
console.log(`Animal moved ${distanceInMeters}m.`);
}
}
class Dog extends Animal {
bark() {
console.log('Woof! Woof!');
}
}
在這個例子中子類Dog使用extends關鍵字繼承了父類Animal的屬性和方法,所以子類的就具有了父類的行爲,子類的實例就可以使用父類的方法。
讓我們開看一個更加複雜一些的例子。
class Animal {
name: string;
constructor(theName: string) { this.name = theName; }
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Snake extends Animal {
constructor(name: string) { super(name); }
move(distanceInMeters = 5) {
console.log("Slithering...");
super.move(distanceInMeters);
}
}
class Horse extends Animal {
constructor(name: string) { super(name); }
move(distanceInMeters = 45) {
console.log("Galloping...");
super.move(distanceInMeters);
}
}
let sam = new Snake("Sammy the Python");
let tom: Animal = new Horse("Tommy the Palomino");
sam.move();
tom.move(34);
輸出:
Slithering...
Sammy the Python moved 5m.
Galloping...
Tommy the Palomino moved 34m.
在這個例子中我們創建了Animal的兩個子類Snake,Horse,並且使用了super關鍵字,typescript約定,如果父類有顯示聲明的構造器,子類在使用this關鍵字之前必須先調用super()來構建父類的實例,如果父類沒有顯示聲明的構造器,則子類默認調用super()。super.property也來使用父類的的方法和屬性。
這個例子也展示了子類可以使用同名的屬性和方法覆蓋父類的屬性和方法,儘管Horse的實例被聲明爲Animal的類型,但因爲它的值是一個Horse,所以它仍然是調用Horse的方法。
3、Public,Private和Protected修飾符
3.1、Public是默認的
如果你熟悉其他語言,你可能會發現我們在以上例子中沒有使用public關鍵字來聲明一個成員,這是因爲在typescript中每一個成員默認是public成員的,上面的例子也可以改寫成以下形式。
class Animal {
public name: string;
public constructor(theName: string) { this.name = theName; }
public move(distanceInMeters: number) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
3.2、理解Private
當一個成員被聲明爲一個private成員的時候,它就不能在它聲明的類之外使用,如果一個構造器被聲明爲一個private成員,那它就不能在它的類外進行實例化。
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}
new Animal("Cat").name; // Error: 'name' is private;
typescript是一個結構形類型系統,當我們比較兩種類型的時候,我們會忽略它們的來源,只要它們的成員是兼容的就可以了,然而當比較含有private和protected的成員類型時,這種情況就變得不同了。
當我們比較這種類型時,只有當他們的private成員是來自同一個聲明時才被認爲是兼容的(同一個類的private成員,不同類的同名private成員是不同的),對於protected成員也是如此。
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}
class Rhino extends Animal {
constructor() { super("Rhino"); }
}
class Employee {
private name: string;
constructor(theName: string) { this.name = theName; }
}
let animal = new Animal("Goat");
let rhino = new Rhino();
let employee = new Employee("Bob");
animal = rhino;
animal = employee; // Error: 'Animal' and 'Employee' are not compatible
3.3、理解Protected
protected修飾符和private很類似,除了它所聲明的成員可以在它的子類中使用之外。
class Person {
protected name: string;
constructor(name: string) { this.name = name; }
}
class Employee extends Person {
private department: string;
constructor(name: string, department: string) {
super(name);
this.department = department;
}
public getElevatorPitch() {
return `Hello, my name is ${this.name} and I work in ${this.department}.`;
}
}
let howard = new Employee("Howard", "Sales");
console.log(howard.getElevatorPitch());
console.log(howard.name); // error
注意我們不能在類外使用protected成員,但可以在它的子類的方法中調用。
當一個類的構造器被聲明爲protected成員時,這意味你不能夠在該類之外實例化該類,但是它能夠被繼承。
class Person {
protected name: string;
protected constructor(theName: string) { this.name = theName; }
}
// Employee can extend Person
class Employee extends Person {
private department: string;
constructor(name: string, department: string) {
super(name);
this.department = department;
}
public getElevatorPitch() {
return `Hello, my name is ${this.name} and I work in ${this.department}.`;
}
}
let howard = new Employee("Howard", "Sales");
let john = new Person("John"); // Error: The 'Person' constructor is protected
4、Readonly修飾符
你可以在屬性前面添加readonly關鍵字使屬性變得只讀的,只讀屬性必須在聲明時實例化或者在構造器中賦值。
class Octopus {
readonly name: string;
readonly numberOfLegs: number = 8;
constructor (theName: string) {
this.name = theName;
}
}
let dad = new Octopus("Man with the 8 strong legs");
dad.name = "Man with the 3-piece suit"; // error! name is readonly.
5、參數屬性
我們可以在構造器參數定義的同時定義一個類的成員,只要在參數的前面加上修飾符即可(readonly,public,private,protected),例如以上例子可以改成如下形式。
class Octopus {
readonly numberOfLegs: number = 8;
constructor(readonly name: string) {
}
}
6、存取器
typescript支持getters/setters,這給了你控制成員獲取和設置的方式。
let passcode = "secret passcode";
class Employee {
private _fullName: string;
get fullName(): string {
return this._fullName;
}
set fullName(newName: string) {
if (passcode && passcode == "secret passcode") {
this._fullName = newName;
}
else {
console.log("Error: Unauthorized update of employee!");
}
}
}
let employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
console.log(employee.fullName);
}
關於存取器有幾點需要注意,首先必須設置編譯器輸出的是es5及更高版本,es3是不支持存取器的。另外,如果只設置get而沒有設置set,這個屬性就自動被設置爲readonly的。
7、靜態屬性
在以上案例中我們使用的都是實例屬性,我們也可以使用static關鍵字設置類的靜態成員,通過使用類名.property來訪問類的靜態成員,類的靜態成員被所有類的實例所共享。
class Grid {
static origin = {x: 0, y: 0};
calculateDistanceFromOrigin(point: {x: number; y: number;}) {
let xDist = (point.x - Grid.origin.x);
let yDist = (point.y - Grid.origin.y);
return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
}
constructor (public scale: number) { }
}
let grid1 = new Grid(1.0); // 1x scale
let grid2 = new Grid(5.0); // 5x scale
console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));
8、抽象類
可以使用abstract關鍵字聲明一個抽象類和抽象方法,和接口不同的是,抽象類可以包含成員的具體實現。被abstract聲明的方法只有函數簽名而沒有方法體,且必須在子類中實現。另外抽象類不可以實例化。
abstract class Department {
constructor(public name: string) {
}
printName(): void {
console.log("Department name: " + this.name);
}
abstract printMeeting(): void; // must be implemented in derived classes
}
class AccountingDepartment extends Department {
constructor() {
super("Accounting and Auditing"); // constructors in derived classes must call super()
}
printMeeting(): void {
console.log("The Accounting Department meets each Monday at 10am.");
}
generateReports(): void {
console.log("Generating accounting reports...");
}
}
let department: Department; // ok to create a reference to an abstract type
department = new Department(); // error: cannot create an instance of an abstract class
department = new AccountingDepartment(); // ok to create and assign a non-abstract subclass
department.printName();
department.printMeeting();
department.generateReports(); // error: method doesn't exist on declared abstract type
9、高級技術
class其實是一個語法糖,聲明一個類實際上創建了兩個東西:實例的類型和和一個構造函數。
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter: Greeter;
greeter = new Greeter("world");
console.log(greeter.greet());
轉化後
let Greeter = (function () {
function Greeter(message) {
this.greeting = message;
}
Greeter.prototype.greet = function () {
return "Hello, " + this.greeting;
};
return Greeter;
})();
let greeter;
greeter = new Greeter("world");
console.log(greeter.greet());
我們也可以使用接口來繼承一個類
````
class Point {
x: number;
y: number;
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};
````