TS系列篇|类(class)
"不畏惧,不将就,未来的日子好好努力"——大家好!我是小芝麻😄
类(Class)定义了一件事物的抽象特点,包含它的属性和方法
1、定义类
在 TypeScript
中,我们也是通过 Class
关键字来定义一个类, 使用 constructor
定义构造函数。
构造函数: constructor
- 主要用于初始化类的成员变量属性
- 类的对象创建时自动调用执行
- 没有返回值
class Animal {
public name: string;
constructor(name: string) {
this.name = name;
}
sayHi(): string {
return `My name is ${this.name}`;
}
}
2、类的继承
使用 extends
关键字实现继承,子类中使用 super
关键字来调用父类的构造函数和方法。
- 子类继承父类后子类的实例就拥有了父类中的属性和方法,可以增强代码的可复用性
- 将子类共用的方法抽象出来放在父类中,自己特殊逻辑放在子类中重写父类的逻辑
- super 可以调用父类上的方法和属性: (相当于ES5的:在静态方法和构造函数中指向父类; 在普通函数中指向父类的prototype;)
class Animal {
public name: string;
constructor(name: string) {
this.name = name;
}
sayHi(): string {
return `My name is ${this.name}`;
}
}
class Cat extends Animal {
constructor(name) {
super(name); // 调用父类的 constructor(name)
console.log(this.name);
}
sayHi(): string { // 将子类共用的方法抽象出来放在父类中,自己特殊逻辑放在子类中重写父类的逻辑
return 'Meow, ' + super.sayHi(); // 调用父类的 sayHi()
}
}
let c = new Cat('Tom'); // Tom
console.log(c.sayHi()); // Meow, My name is Tom
2.1 重写(override) VS 重载(overload)
- 重写是指子类重写继承自父类的方法
- 重载是指为同一个函数提供多个类型定义
2.2 继承 VS 多态
- 继承(inheritance)子类继承父类,子类除了拥有父类的所有特性外,还有一些具体的特性
- 多态(Polymorphism)由继承而产生了相关的不同的类,对同一个方法可以有不同的行为
3、类的修饰符
TypeScript
中有三类访问修饰符,分别是: public
、private
、protected
。不写默认为 public
- public :自己、自己的子类 和其他类都可以访问 (默认值)
- protected 受保护的 自己和自己的子类能访问, 其他类不能访问
- private 私有的 只能自己访问,自己的子类不能访问,其他类更不能访问
class Father {
public name: string
protected age: number
private money: number
constructor(name: string, age: number, money: number) {
this.name = name
this.age = age
this.money = money
}
getName(): string {
return this.name
}
setName(name: string): void {
this.name = name
}
}
class child extends Father {
constructor(name: string, age: number, money: number) {
super(name, age, money)
}
desc() {
console.log(`${this.name}${this.age}${this.money}`)
// 属性“money”为私有属性,只能在类“Father”中访问
}
}
let children = new child('金色小芝麻', 18, 1000)
console.log(children.name)
console.log(children.age) // ERROR 属性“age”受保护,只能在类“Father”及其子类中访问。
console.log(children.money) // ERROR 属性“money”为私有属性,只能在类“Father”中访问。
- 当构造函数修饰为
private
时,该类不允许被继承或者实例化:
class Animal {
public name: string;
private constructor(name: string) {
this.name = name;
}
}
class Cat extends Animal { // 无法扩展类“Animal”。类构造函数标记为私有。
constructor(name: string) {
super(name);
}
}
let a = new Animal('Jack'); // 类“Animal”的构造函数是私有的,仅可在类声明中访问。
- 当构造函数修饰为
protected
时,该类只允许被继承:
class Animal {
public name: string;
protected constructor(name: string) {
this.name = name;
}
}
class Cat extends Animal {
constructor(name: string) {
super(name);
}
}
let a = new Animal('Jack'); // 类“Animal”的构造函数是受保护的,仅可在类声明中访问。
4、readonly
readonly 修饰的变量只能在属性声明时或 构造函数
中初始化
class Animal {
// 如果只读修饰符和可见性修饰符同时出现,需要将只读修饰符写在可见修饰符后面。
public readonly name: string
constructor(name: string) {
this.name = name
}
changeName(name: string) {
this.name = name // ERROR 无法分配到 "name" ,因为它是只读属性。
}
}
let a = new Animal('hello')
a.name = '111' // ERROR 无法分配到 "name" ,因为它是只读属性
a.changeName('nihao') // ERROR 无法分配到 "name" ,因为它是只读属性
console.log(a)
readonly 只是在 编译
阶段进行代码检查。运行时依然能打印
- 编译时:
- 运行时:
5、参数属性
在上面的例子中,都是在类的定义的顶部初始化实例属性,在 constructor
里接收参数然后对实例属性进行赋值,参数属性就是为了简化这一过程的
直接在
constructor
构造函数的参数前面加上修饰符或readonly
=> 等同于在类中定义该属性同时给该属性赋值,使代码更简洁。
class User {
constructor(public name: string) {}
}
let user = new User('hello')
console.log(user.name) // hello
user.name = 'nihao'
console.log(user.name) // nihao
6、存取器
- 在
TypeScript
中,我们可以通过getter
/setter
来改变一个类中属性的读取和赋值行为
class Person {
name: string
constructor(name: string) {
this.name = name
}
get getName() { // 读取
return this.name
}
set setName(val: string) { // 赋值
this.name = val.toUpperCase()
}
}
let p1 = new Person('nihao')
console.log(p1.getName)
p1.setName = 'hello'
console.log(p1.name)
7、静态属性和静态方法static
使用 static
修饰符修饰的方法称为静态方法,它们不需要实例化,而是直接通过类来调用:
class Father {
public name: string
constructor(name: string) {
this.name = name
}
public static className: string = 'Father'
static getClassName(): string {
return Father.className
}
}
console.log(Father.className)
console.log(Father.getClassName())
8、抽象类和抽象方法abstract
使用
abstract
关键字来定义抽象类和在抽象类内部定义抽象方法。
8.1 抽象类
- 抽象描述一种抽象的概念,做为其它类的基类使用
- 无法创建抽象类的实例,抽象类只能被继承
abstract class Animal {
name!: string
abstract speak(): void
}
class Cat extends Animal {
speak() {
console.log('喵喵喵')
}
}
let animal = new Animal(); // ERROR 无法创建抽象类的实例
let cat = new Cat()
cat.speak() // 喵喵喵
8.2 抽象方法
- 抽象方法不能在抽象类中实现,只能在抽象类的具体子类中实现,而且必须实现
- 抽象方法只能出现在抽象类中
abstract class Animal {
name: string
abstract speak(): void = ()=>{} // ERROR 方法“speak”不能具有实现,因为它标记为抽象。
}
class Cat extends Animal {
speak() { // 在这里实现 speak 方法
console.log('喵喵喵')
}
}
- 子类可以对抽象类进行不同的实现
abstract class Animal {
abstract speak(): void
}
class Dog extends Animal {
speak() {
console.log('汪汪汪')
}
}
class Cat extends Animal {
speak() {
console.log('喵喵喵')
}
}
8.3 抽象类 VS 接口
- 不同类之间公有的属性或方法,可以抽象成一个接口(interfaces), 而抽象类是供其他类继承的基类
- 抽象类本质是一个无法被实例化的类,可以包含成员的实现细节,而接口仅能够用于描述,既不能提供方法的实现细节,也不为属性进行初始化
- 一个类可以继承一个类或抽象类,但可以实现(implements)多个接口
- 抽象类也可以实现接口
abstract class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
abstract speak(): void;
}
interface Flying {
fly(): void
}
interface age {
age: number
}
class Dog extends Animal implements Flying, age {
age: 18
speak() {
console.log('汪汪汪')
}
fly() {
console.log('我会飞')
}
}
访问控制修饰符 | private、protected、public |
只读属性 | readonly |
静态属性 | static |
抽象类、抽象方法 | abstract |
参考文献
[1]. TypeScript中文网
[2]. TypeScript 入门教程
转载自:https://juejin.cn/post/7006482612306837534