雖然 JavaScript 中有類的概念,可是可能大多數 JavaScript 程序員並非很是熟悉類,這裏對類相關的概念作一個簡單的介紹。程序員
new
生成Cat
和 Dog
都繼承自 Animal
,可是分別實現了本身的 eat
方法。此時針對某一個實例,咱們無需瞭解它是 Cat
仍是 Dog
,就能夠直接調用 eat
方法,程序會自動判斷出來應該如何執行 eat
public
表示公有屬性或方法使用 class
定義類,使用 constructor
定義構造函數。函數
經過 new
生成新實例的時候,會自動調用構造函數。this
class Animal { constructor(name) { this.name = name; } sayHi() { return `My name is ${this.name}`; } } let a = new Animal('Jack'); console.log(a.sayHi()); // My name is Jack
使用 extends
關鍵字實現繼承,子類中使用 super
關鍵字來調用父類的構造函數和方法。3d
class Cat extends Animal { constructor(name) { super(name); // 調用父類的 constructor(name) console.log(this.name); } sayHi() { return 'Meow, ' + super.sayHi(); // 調用父類的 sayHi() } } let c = new Cat('Tom'); // Tom console.log(c.sayHi()); // Meow, My name is Tom
使用 static
修飾符修飾的方法稱爲靜態方法,它們不須要實例化,而是直接經過類來調用:code
class Animal { static isAnimal(a) { return a instanceof Animal; } } let a = new Animal('Jack'); Animal.isAnimal(a); // true a.isAnimal(a); // TypeError: a.isAnimal is not a function
ES6 中實例的屬性只能經過構造函數中的 this.xxx
來定義,ES7 提案中能夠直接在類裏面定義:對象
class Animal { name = 'Jack'; constructor() { // ... } } let a = new Animal(); console.log(a.name); // Jack
ES7 提案中,可使用 static
定義一個靜態屬性:繼承
class Animal { static num = 42; constructor() { // ... } } console.log(Animal.num); // 42
TypeScript 可使用三種訪問修飾符(Access Modifiers),分別是 public
、private
和 protected
。接口
public
修飾的屬性或方法是公有的,能夠在任何地方被訪問到,默認全部的屬性和方法都是 public
的ip
class Animal { public name; public constructor(name) { this.name = name; } } let a = new Animal('Jack'); console.log(a.name); // Jack a.name = 'Tom'; console.log(a.name); // Tom
上面的例子中,name
被設置爲了 public
,因此直接訪問實例的 name
屬性是容許的。get
private
修飾的屬性或方法是私有的,不能在聲明它的類的外部訪問。
不少時候,咱們但願有的屬性是沒法直接存取的,這時候就能夠用 private
了:
class Animal { private name; public constructor(name) { this.name = name; } } let a = new Animal('Jack'); console.log(a.name); // Jack a.name = 'Tom'; // index.ts(9,13): error TS2341: Property 'name' is private and only accessible within class 'Animal'. // index.ts(10,1): error TS2341: Property 'name' is private and only accessible within class 'Animal'.
須要注意的是,TypeScript 編譯以後的代碼中,並無限制 private
屬性在外部的可訪問性。
上面的例子編譯後的代碼是:
var Animal = (function () { function Animal(name) { this.name = name; } return Animal; }()); var a = new Animal('Jack'); console.log(a.name); a.name = 'Tom';
使用 private
修飾的屬性或方法,在子類中也是不容許訪問的:
class Animal { private name; public constructor(name) { this.name = name; } } class Cat extends Animal { constructor(name) { super(name); console.log(this.name); } } // index.ts(11,17): error TS2341: Property 'name' is private and only access
protected
修飾的屬性或方法是受保護的,它和 private
相似,區別是它在子類中也是容許被訪問的
class Animal { protected name; public constructor(name) { this.name = name; } } class Cat extends Animal { constructor(name) { super(name); console.log(this.name); } }
你可使用readonly
關鍵字將屬性設置爲只讀的。 只讀屬性必須在聲明時或構造函數裏被初始化。
class Animal { readonly name: string; public constructor(name: string) { this.name = name; } } let cat = new Animal('Tom'); cat.name = 'jack'; //Cannot assign to 'name' because it is a read-only property.ts(2540)
抽象類是供其它類繼承的基類。 他們通常不會直接被實例化。 不一樣於接口,抽象類能夠包含成員的實現細節。 abstract
關鍵字是用於定義抽象類和在抽象類內部定義抽象方法。
abstract class Animal { abstract makeSound(): void; move(): void { console.log('roaming the earch...'); } }
抽象類中的抽象方法不包含具體實現而且必須在派生類中實現。 抽象方法的語法與接口方法類似。 二者都是定義方法簽名不包含方法體。 然而,抽象方法必須使用abstract
關鍵字而且能夠包含訪問符。
abstract class Department { constructor(public name: string) { } printName(): void { console.log('Department name: ' + this.name); } abstract printMeeting(): void; // 必須在派生類中實現 } 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
給類加上 TypeScript 的類型很簡單,與接口相似:
class Animal { name: string; constructor(name: string) { this.name = name; } sayHi(): string { return `My name is ${this.name}`; } } let a: Animal = new Animal('Jack'); console.log(a.sayHi()); // My name is Jack
實現(implements)是面向對象中的一個重要概念。通常來說,一個類只能繼承自另外一個類,有時候不一樣類之間能夠有一些共有的特性,這時候就能夠把特性提取成接口(interfaces),用 implements
關鍵字來實現。這個特性大大提升了面向對象的靈活性。
舉例來講,門是一個類,防盜門是門的子類。若是防盜門有一個報警器的功能,咱們能夠簡單的給防盜門添加一個報警方法。這時候若是有另外一個類,車,也有報警器的功能,就能夠考慮把報警器提取出來,做爲一個接口,防盜門和車都去實現它:
interface Alarm { alert(); } class Door { } class SecurityDoor extends Door implements Alarm { alert() { console.log('SecurityDoor alert'); } } class Car implements Alarm { alert() { console.log('Car alert'); } }
一個類能夠實現多個接口:
interface Alarm { alert(); } interface Light { lightOn(); lightOff(); } class Car implements Alarm, Light { alert() { console.log('Car alert'); } lightOn() { console.log('Car light on'); } lightOff() { console.log('Car light off'); } }
上例中,Car
實現了 Alarm
和 Light
接口,既能報警,也能開關車燈。
接口與接口之間能夠是繼承關係:
interface Alarm { alert(); } interface LightableAlarm extends Alarm { lightOn(); lightOff(); }
上例中,咱們使用 extends
使 LightableAlarm
繼承 Alarm
。
接口也能夠繼承類:
class Point { x: number; y: number; } interface Point3d extends Point { z: number; } let point3d: Point3d = {x: 1, y: 2, z: 3};
可使用接口的方式來定義一個函數須要符合的形狀:
interface SearchFunc { (source: string, subString: string): boolean; } let mySearch: SearchFunc; mySearch = function(source: string, subString: string) { return source.search(subString) !== -1; }
有時候,一個函數還能夠有本身的屬性和方法:
interface Counter { (start: number): string; interval: number; reset(): void; } function getCounter(): Counter { let counter = <Counter>function (start: number) { }; counter.interval = 123; counter.reset = function () { }; return counter; } let c = getCounter(); c(10); c.reset(); c.interval = 5.0;