背景简介
传统的JavaScript程序使用函数和基于原型的继承来创建可重用的组件,但对于熟悉使用面向对象方式的程序员来讲就有些棘手, 因为他们用的是基于类的继承并且对象是由类构建出来的。 从ECMAScript 2015,也就是ECMAScript 6开始, JavaScript程序员将能够使用基于类的面向对象的方式。 使用TypeScript,我们允许开发者现在就使用这些特性, 并且编译后的JavaScript可以在所有主流浏览器和平台上运行,而不需要等到下个JavaScript版本。
风格约定
- 1.类名首字母大写
- 2.使用
new关键字对类实例化操作
成员结构
class Person {
name:string; // 1.属性
constructor(msg:string){ // 2.构造函数
this.name=msg;
}
sayHello(){ // 3.方法
return `hello,${this.name}`
}
}
let p1=new Person('小宁');
console.log(p1.sayHello())
继承
class Animal { // Animal在这里称为基类或超类
move(distanceInMeters: number = 0) {
console.log(`Animal moved ${distanceInMeters}m.`);
}
}
class Dog extends Animal { // Dog称为派生类或子类
bark() {
console.log('Woof! Woof!');
}
}
const dog = new Dog();
dog.bark();
dog.move(10);
继承使用super关键字
class Animal {
type:string;
constructor(msg:string) {
this.type=msg;
}
sayName(){
console.log(`我的类型为:${this.type}`)
}
}
class Dog extends Animal {
constructor(name:string){super(name)}
}
class Horse extends Animal {
constructor(name:string){super(name)}
}
let d1=new Dog('小狗');
// 即使d2被声明为Dog类型,但因为它的值是Horse,它会调用 Horse里重写的方法:
let d2:Dog=new Horse('小马');
d1.sayName()
d2.sayName();
公共、私有、受保护
- 公共
public(默认)
class Animal {
public name: string;
public constructor(theName: string) { this.name = theName; }
public move(distanceInMeters: number) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
- 私有
private
当成员被标记成 private时,它就不能在声明它的类的外部访问
class Animal {
private name: string;
constructor(msg: string) { this.name = msg; }
}
let a1=new Animal('小牛');
console.log(a1.name) // 报错 提示name为私有属性
- 受保护
protected
protected修饰符与 private修饰符的行为很相似,但有一点不同, protected成员在派生类中仍然可以访问
class Animal {
protected name: string;
constructor(msg: string) { this.name = msg; }
}
class Cat extends Animal {
constructor(name:string){super(name)}
public print():string{
return `父类的name为:${this.name}`;
}
}
let c1=new Cat('小猫');
console.log(c1.name) // 不可外部访问父类或者子类的protected属性,会提示只可内部访问
let a1=new Cat('动物');
console.log(a1.print()); // 在子类的内部方法中,是可以访问父类protected成员的
只读属性
readonly可以将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。
class Person { // 情况1:正常代码
readonly name:string;
readonly age:number=20;
constructor(msg:string){
this.name=msg;
console.log(this.name)
}
}
let p1=new Person('小明');
class Person { // 情况2:readonly简化声明赋值代码
readonly age:number=20;
constructor(readonly name:string){
console.log(this.name)
}
}
let p1=new Person('小红');
存取器
TS支持通过getters/setters来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问
class DateSource {
private _userName:string='default';
get username():string{
return this._userName;
}
set username(msg:string){
this._userName=msg
}
}
let d1=new DateSource();
console.log(d1.username) // 使用get实现访问外部不可访问的变量
d1.username='update'; // 使用set实现修改赋值操作
console.log(d1.username) // 再次打印查看效果
静态属性
超类中
static修饰的属性不可被this.访问,只可以使用类名.访问·
class Person {
static width:number=200;
public printStyle(){
console.log(`打印width==${this.width}`) // 报错 不可以使用this.
console.log(`打印width==${Person.width}`) // 正确 使用类名.格式
}
}
let p1=new Person();
p1.printStyle();
抽象类
使用
abstract关键字
abstract class Person { // abstract声明抽象类
public name:string;
constructor(msg:string){this.name=msg};
abstract printMeeting():void; // abstract实现方法
}
class Man extends Person {
constructor(){super('man')}
printMeeting():void{ // 子类必须实现父类创造的方法,因为那是父类给子类定义的标准
console.log('man必须实现方法体')
}
}
let p1:Person; // 正确 可以引用抽象类的类型
let p1=new Man(); // 正确 允许实例抽象类的子类
let p1=new Person('人'); // 错误 抽象类不可以被实例,只能继承
把类当做接口使用
class Point {
x: number;
y: number;
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};