传统的 JavaScript 程序使用函数和基于原型的继承来创建可重用的组件,从 ES6 开始,JavaScript 程序能够使用基于类的面向对象的方式。使用 TypeScript,你可以使用 ES6 中规定的新特性,编译后的 JavaScript 可以在所有主流浏览器和平台上运行。
基本用法
class Person {
public love: string;
constructor(love: string) {
this.love = love;
}
public sayLove() {
console.log(`my love is ${this.love}`)
}
}
继承
在构造器里访问 this 的属性之前,一定要调用 super() ,这个是 TypeScript 强制执行的一条重要规则。
class Person {
public love: string;
constructor(love: string) {
this.love = love;
}
public sayLove() {
console.log(`my love is ${this.love}`)
}
}
class SuperPerson extends Person {
public name: string;
constructor(love: string, name: string) {
super(love);
this.name = name;
}
public sayName(){
console.log(`my name is ${this.name}`)
}
}
let me = new SuperPerson('HTML', 'funlee');
me.sayLove()
me.sayName()
访问控制
public、private、protected
默认是 public, 不再赘述,参考前面例子。
当成员标记为 private 时,它就不能在声明它的类的外部访问,用 protected 修饰的属性依然如此。
class Person {
private love: string; // or prot
constructor(love: string) {
this.love = love;
}
public sayLove() {
console.log(`my love is ${this.love}`)
}
}
let me = new Person('TS');
me.love = 'JS'; // error
private 和 protected 有一点不同, protected 成员在派生类中仍然可以访问。例如:
class Person {
protected name: string;
constructor(name: string) {
this.name = name;
}
}
class Man extends Person {
private love: string;
constructor(name: string, love: string) {
super(name);
this.love = love;
}
public say() {
// 如果Person 中用 private 修饰 name 则不能访问到 name 属性
console.log(`my name is ${this.name}, and my love is ${this.love}`);
}
}
let me = new Man('funlee', 'TS');
注意:TypeScript 使用的是结构性类型系统,所以当比较两种不同的类型时,如果所有的成员的类型都是兼容的,那么这两个类型就是兼容的。如:
class A {
prop1: string
}
class B {
prop1: string
prop2: string
}
let instance:A = new B() // 允许这么做,因为A的所有成员类型,B中都有
但是如果被比较的类里面含有 private 和 protected 类型成员的时候,情况就不同了,这时候需要另一个类里也含有相应的 private 或 protected 成员,类型才能是兼容的,所以有:
class A {
private prop1: string
}
class B {
private prop2: string
}
let p1:A = new B() // 报错
class C extends A {
}
let p2:A = new C() // 允许这么做
readonly
可以使用 readonly 关键字将属性设置为只读的,只读属性必须在声明时或构造函数里被初始化。
class Person {
readonly name: string;
constructor(name: string) {
this.name = name;
}
}
let me = new Person('funlee');
me.name = 'new name'; // error
参数属性
参数属性允许同时创建和初始化成员,可以把声明和赋值合并至一处,如:
class Person {
constructor(public name: string, protected love: string, readonly age: number, private weight: string) {
this.name = name;
this.love = love;
this.age = age;
}
public sayWeight() {
console.log(`my weight is ${this.weight}`)
}
}
let me = new Person('funlee', 'TS', 18, '55kg');
me.sayWeight()
存取器
TypeScript 支持 getter 和 setter,但是有一点限制:编译器输出必须设为 ES5 或者更高,不支持降级到 ES3,另外,当一个存取器只带有 get 却不带有 set 时,它会被自动推断为 readonly。
class Person {
public _love: string;
constructor(love: string) {
this._love = love;
}
get love(): string{
return this._love;
}
set love(newLove: string) {
this._love = `error!! my love can't be chenged`;
}
}
let me = new Person('TS');
console.log(me.love); // TS
me.love = 'HTML';
console.log(me.love); // error!! my love can't be chenged
静态属性
可以使用static来定义类里的静态属性,静态属性属于类自身,而不属于实例,访问的时候要用类名访问,而不能用实例对象访问,如:
class Person {
static love: string = 'TS';
}
let me = new Person();
console.log(Person.love); // TS
console.log(me.love); // error
抽象类
抽象类只能作为其他派生类的基类使用,抽象类不能被实例化,它具有如下特点:
- 抽象类可以包含成员的实现细节,且抽象类必须用 abstract 声明
- 抽象类里不含方法体的方法称为抽象方法,使用 abstract 声明,抽象方法必须被子类实现(抽象方法必须使用 abstract 关键字声明,且可以包含访问修饰符)
abstract class Person {
public love: string;
constructor(love: string) {
this.love = love;
}
abstract sayLove(): string; // 必须在派生类中实现
}
class Man extends Person{
constructor(love: string){
super(love)
}
sayLove() {
return `my love is ${this.love}`;
}
}
let me = new Man('TS');
console.log(me.sayLove()); // my love is TS
把类当做接口使用
类定义会创建两个东西:类的实例类型和一个构造函数,因为类可以创建出类型,所以能够在允许使用接口的地方使用类。
class Person {
name: string;
age: number;
}
interface Man extends Person {
love: string;
}
let me: Man = {
name: 'funlee',
age: 18,
love: 'TS'
}