ts的类基本包括了es6+中类的全部功能。假如我要声明一条“狗”类,在ts中实现一个类是这样的:
class Dog {
constructor(name: string) {
this.name = name
}
name: string
run() {
console.log(`${this.name} is running`)
}
}
const dog = new Dog("Bobby")
类属性都是实例属性,而不是原型属性。ts中属性必须被初始化。
如果为狗类声明一个子类(哈士奇类),并且有哈士奇有它的属性:颜色:
class Hasky extends Dog {
constructor(name: string, color: string) {
super(name)
this.color = color
}
color: string
}
const hasky = new Hasky("haha", "white")
hasky.run() // haha is running
console.log(hasky.color) // white
类所有默认的修饰符都是public。表示作用域内所有成员可见。如:
class Dog {
constructor(name: string) {
this.name = name
}
public name: string // 类型注解
run() {
console.log(`${this.name} is running`)
}
}
可以不写。在构造函数的参数上使用public
等同于创建了同名的成员变量。
表示为一个类的私有成员,只有类本身内部可以调用,子类和实例无法调用:
class Dog {
constructor(name: string) {
this.name = name
}
public name: string // 类型注解
// 私有成员
private eat() {
console.log(`dog is eating`)
}
run() {
console.log(`${this.name} is running`)
}
}
如果private修饰constructor,表示这个类既不能被实例化,也不能被继承。
Protected修饰需要保护的成员——只能在类和子类中访问,但不能在它们的实例中访问
如果protected修饰的contructor,那么表示它不能被实例化,而只能被继承。也就是所谓的“基类”。
只读属性。不可以被修改,必须在属性中被实例化。
修饰符也可以用于修饰参数。比如我在参数重直接public color,就不必再申明了,构造函数的写法都可以不写了。
class Hasky extends Dog {
constructor(name: string, public color: string) {
super(name)
}
}
静态成员只能通过类名来调用,可以被继承。
class Dog {
constructor(name: string) {
this.name = name
}
public name: string // 类型注解
private eat() {
console.log(`dog is eating`)
}
protected sleep() {}
static food: string = "aaa"
run() {
console.log(`${this.name} is running`)
}
}
ts的抽象类是对js的又一扩展。如果做这么一个类比:有一个“动物”类,它抽象概括了大部分生物的特征,但它没法被实例化,只有通过”狗“类,“人”类等子类来使之形象化。
因而,可以说抽象类是只能被继承但不能实例化的类。在ts中,通过abstract来声明抽象类。
abstract class Animal {
constructor() {}
shout() {
console.log("shout")
}
}
class Dog extends Animal {
constructor(public name: string) {
super()
}
run() {
console.log(`${this.name} is running`)
}
}
const dog = new Dog("Bobby")
dog.shout()
抽象类中,也可以不指定方法等具体实现,这就构成了抽象方法。子类有此实现,就没必要在在父类中实现了。
abstract class Animal {
constructor() {}
abstract shout(): void
}
class Cat extends Animal {
constructor() {
super()
}
shout() {
console.log("miao")
}
}
class Dog extends Animal {
constructor() {
super()
}
shout() {
console.log("wang")
}
}
const animals: Animal[] = [new Cat(), new Dog()]
animals.forEach((animal) => {
animal.shout()
})// miao wang
上面的例子中,给Animal定义了一个shout方法。但是具体的实现交给了子类来完成。从这个意义来说,模板方法模式的思想很好地得到了实现。
不单类可以多态,this也可以多态。我想在ts中实现类似jquery的链式调用。和js写法一致,思路依然是在原型方法中return this
。
class WorkFlow {
step1() {
return this
}
step2() {
return this
}
}
new WorkFlow().step1().step2()
如果在子类中这么做,this可以同时调用子类和父类的方法:
class MyFlow extends WorkFlow {
next() {
return this
}
}
new MyFlow().step1().next().step2()
实现(implements)是面向对象中的一个重要概念。一般来讲,一个类只能继承自另一个类,有时候不同类之间可以有一些共有的特性,这时候就可以把特性提取成接口(interfaces),用 implements
关键字来实现。这个特性大大提高了面向对象的灵活性。
举例来说,门是一个类,防盗门是门的子类。如果防盗门有一个报警器的功能,我们可以简单的给防盗门添加一个报警方法。这时候如果有另一个类,车,也有报警器的功能,就可以考虑把报警器提取出来,作为一个接口,防盗门和车都去实现它。
通过接口,可以约束类成员有哪些属性,还有哪些类型。比如说,我先定义一个“人”类,包括name属性(string),和eat方法(void)。然后通过implements
关键字,按照这个接口去实现“亚洲人”类。
interface Human {
name: string
eat(): void
}
class Asian implements Human {
constructor(public name: string) {}
eat() {}
}
注意,类必须要完整声明完接口声明的成员(公有成员,不可改动)。如果类有自己的属性,这是可以的。也不能在接口定义构造函数。
interface Human {
name: string
eat(): void
}
class Asian implements Human {
constructor(public name: string) {}
eat() {}
}
interface American extends Human {
run(): void
}
interface European extends Human {
cry(): void
}
interface African extends American, European {}
// 必须完整定义完父接口的方法
const p: African = {
name: "",
run() {},
cry() {},
eat() {},
}
这里p实际上也是被编译为一个对象。
接口继承类,然后再用一个类来继承这个接口:
class Auto {
state = 1
}
interface AutoInterface extends Auto {}
class C implements AutoInterface {
state = 1
}
在此,如果C不定义state,将会报错。
下图反映了接口和类的关系: