类相关概念
- 类(Class):定义了一件事物的抽象特点,包含它的属性和方法
- 对象(Object):类的实例,通过 new 生成
- 面向对象(OOP)的三大特性:封装、继承、多态
- 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据
- 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
- 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。比如 Cat 和 Dog 都继承自 Animal,但是分别实现了自己的 eat 方法。此时针对某一个实例,我们无需了解它是 Cat 还是 Dog,就可以直接调用 eat 方法,程序会自动判断出来应该如何执行 eat
- 存取器(getter & setter):用以改变属性的读取和赋值行为
- 修饰符(Modifiers):修饰符是一些关键字,用于限定成员或类型的性质。比如 public 表示公有属性或方法
- 抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现
- 接口(Interfaces):不同类之间公有的属性或方法,可以抽象成一个接口。接口可以被类实现(implements)。一个类只能继承自另一个类,但是可以实现多个接口
类直接作为类型实际上就是实例的类型,而typeof class则是完整的类的类型(包括prototype等)
TS中使用类
1. 三种访问修饰符
public 默认的都是public,可以在类的
**外部**和**内部**使用- private 只能在类的
**内部**使用,**外部**和**继承**中都不可以使用 - protected 除了在
**外部**不可使用 ```javascript class Animal { private name; public constructor(name) { this.name = name; } } // private修饰的不允许被访问 let a = new Animal(“Jack”); console.log(a.name); a.name = “Tom”; // private修饰的在子类中也不允许被访问 class Cat extends Animal { constructor(name) { super(name); console.log(this.name); // // 报错:属性“name”为私有属性,只能在类“Animal”中访问。 } }
// 当构造函数修饰为 private 时,该类不允许被继承或者实例化: class Animal { public name; private constructor(name) { this.name = name; } } class Cat extends Animal { // 报错:无法扩展类“Animal”。类构造函数标记为私有。 constructor(name) { super(name); } } let a = new Animal(‘Jack’); // 报错:类“Animal”的构造函数是私有的,仅可在类声明中访问。
```javascript// 用 protected 修饰,则允许在子类中访问:class Animal {protected name;public constructor(name) {this.name = name;}}class Cat extends Animal {constructor(name) {super(name);console.log(this.name);}}// 当构造函数修饰为 protected 时,该类只允许被继承:class Animal {public name;protected constructor(name) {this.name = name;}}class Cat extends Animal {constructor(name) {super(name);}}let a = new Animal('Jack'); // 报错:类“Animal”的构造函数是受保护的,仅可在类声明中访问。
2. readonly
readonly指定属性为只读,初始化后就不能再修改(const),可以在声明时的时候就初始化它们:
function foo(config: { readonly bar: number, readonly bas: number }) {// ..}const config = { bar: 123, bas: 123 }; // 初始化后,后面不能再改变foo(config);class Foo {readonly bar = 1; // 也可以在声明的时候初始化 OKreadonly baz: string = 'world';constructor() { // ⭐constuctor中的操作都是初始化this.baz = 'hello'; // OK}// 只含有 getter 但是没有 setter 的属性,自动被推断为只读:class Person {get fullName() {return this.firstName + this.lastName;}}
可以搞一个Readonly 的映射类型,接收一个泛型 T,用来把它的所有属性标记为只读类型:
type Foo = {bar: number;bas: number;};const foo: Foo = { bar: 123, bas: 456 };foo.bar = 456; // oktype FooReadonly = Readonly<Foo>;const fooReadonly: FooReadonly = { bar: 123, bas: 456 };fooReadonly.bar = 456; // Error: bar 属性只读
TypeScript 提供ReadonlyArray<T>接口,相当于readonly <T>[],不允许操作数组的索引,但是可以给数组重新赋值:
let foo: ReadonlyArray<number> = [1, 2, 3];console.log(foo[0]); // okfoo.push(4); // Error: 类型“readonly number[]”上不存在属性“push”。foo = [1, 2]; // ok, 重新赋值不受影响foo[0] = 4 // Error:类型“readonly number[]”中的索引签名仅允许读取。
3. 参数修饰符
修饰符和readonly还可以使用在构造函数参数中,等同于类中定义该属性同时给该属性赋值,使代码更简洁:
class Animal {// public name: string;public constructor(public name) {// this.name = name;}}// 注意如果 readonly 和其他访问修饰符同时存在的话,需要写在其后面。class Animal {// public readonly name;public constructor(public readonly name) {// this.name = name;}}
3. 抽象类
abstract 用于定义抽象类和其中的抽象方法。
- 抽象类是不允许被实例化的: ```javascript abstract class Animal { public name; public constructor(name) { this.name = name; } public abstract sayHi(); }
let a = new Animal(‘Jack’); // 报错:无法创建抽象类的实例。
- 抽象类中的抽象方法必须被子类实现:```javascriptabstract class Animal {public name;public constructor(name) {this.name = name;}public abstract sayHi();}class Cat extends Animal {// 报错:非抽象类“Cat”不会实现继承自“Animal”类的抽象成员“sayHi”。public eat() {console.log(`${this.name} is eating.`);}}
