虽然 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;