TypeScript中类的使用详解

一、类的概述

在早期的JavaScript开发中(ES5)需要通过函数和原型链来实现类和继承。

ES6开始,引入了class关键字,可以更加方便的定义和使用类。

TypeScriptJavaScript的超集,也支持使用class关键字,还支持对类的属性和方法等进行静态类型检测。

**虽然在JavaScript的开发过程中,更加习惯于函数式编程,而不是面向对象编程: **

  • React开发中,目前更多使用的函数组件以及结合Hook的开发模式

  • 比如在Vue3开发中,目前也更加推崇使用Composition API

但是在封装某些业务的时候,类也具有更强大封装性。

类的定义我们通常会使用class关键字:

  • 在面向对象的编程中,任何事物都可以使用类的结构来描述

  • 类中可以包含一些自己特有的属性和方法

  • 类也很好的诠释了面向对象的三大特性,继承、封装、多态

二、类的定义

2.1 使用class关键字来定义一个类;

class Teacher {

}

2.2 在类的内部声明类的属性以及对应的类型

class Teacher {
  name: string
  age: number
}

如果类型没有声明,那么它们默认是any的;

类的属性可以设置初始化值:

class Teacher {
  name: string = "zs"
  age: number = "18"
}

在默认的strictPropertyInitialization模式下类的属性是必须初始化的,否则编译时就会报错;

如果在strictPropertyInitialization模式下确实不希望给属性初始化,可以使用name!: string语法。

class Teacher {
  name!: string
  age!: number
}

2.3 声明类的构造函数

class Teacher {
  name: string = "zs"
  age: number = "18"

  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
}

类中的构造函数constructor,当通过new关键字创建一个类的实例时,构造函数会被调用

构造函数不需要返回任何值,默认返回当前创建出来的实例

2.3 定义类中的方法

class Teacher {
  name: string = "zs"
  age: number = "18"

  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }

  eating() {
    console.log(this.name + " eating")
  }
}

类中可以定义一些自己的函数,定义的函数称之为方法

2.4 类的基本使用

const p = new Teacher("ls", 25)
console.log(p.name, p.age)
p.eating()

三、类的继承

面向对象的其中一大特性就是继承,继承不仅仅可以减少我们的代码量,也是多态的使用前提。

TypeScript中使用extends关键字来实现继承,子类中使用super来访问父类中的方法。

注意点:

TypeScript中的类只支持单继承,意为一个类只能继承一个父类,而不能继承多个。

3.1 继承的基本写法

// 定义父类Person
class Person {
  name: string = ""
  age: number = 0

  eating() {
    console.log("eating")
  }
}

// 定义子类Teacher
class Teacher extends Person {
  title: string = ""
    
  constructor(name: string, age: number, title: string) {
    // super调用父类的构造器,对父类中的属性进行初始化
    super(name, age)
    this.title = title
  }

    
  teaching() {
    console.log("teaching")
  }
}

// 使用Teacher
const theacher = new Teacher('zs', 25, '金牌教师')
console.log(theacher.name, theacher.age, theacher.title)
theacher.eating()

Student类可以有自己的属性和方法,并且会继承Teacher的属性和方法;

在构造函数中,我们可以通过super来调用父类的构造方法,对父类中的属性进行初始化;

3.2 方法重写

在上面的代码关系中,theacher.eating()的结果是eating,完全来自于父类中的eating()方法。

此时子类可以自己重新实现属于自己的eating()方法,这就是函数重写。

// 定义子类Teacher
class Teacher extends Person {
  title: string = ""
    
  constructor(name: string, age: number, title: string) {
    // super调用父类的构造器,对父类中的属性进行初始化
    super(name, age)
    this.title = title
  }

  teaching() {
    console.log("teaching")
  }
    
  // 重写父类中的eating方法
  eating() {
    // 有需要的话,可以使用super让父类的eating方法也执行一次
    super.eating()
    // 重写的部分
    console.log("teacher eating")
  }
}

四、类的多态

class Animal {
  action() {
    console.log("animal action")
  }
}

class Dog extends Animal {
  action() {
    console.log("dog running!!!")
  }
}

class Fish extends Animal {
  action() {
    console.log("fish swimming")
  }
}

function makeActions(animals: Animal[]) {
  animals.forEach(animal => {
    animal.action()
  })
}

// Dog和Fish都继承于Animal,本质也是一种Animal
// 根据多态,父类引用可以指向子类的对象,能接收父类作为参数的,也能够接收子类作为参数
// 参数中的new Dog()的返回值是一个Animal animal,相当于Animal animal = new Dog()
// 但是执行action的时候,执行的是具体的那个new出来的对象中的action
makeActions([new Dog(), new Fish(), new Animal()])

多态的目的是为了写出更加具备通用性的代码

多态的重要特点就是父类引用可以指向子类的对象,子类对象可以代替父类出现的地方

五、类中的成员的修饰符

TypeScript中,类的属性和方法支持三种修饰符: publicprivateprotected

  • public 修饰的是在任何地方可见、公有的属性或方法,默认编写的属性就是public

    class Person {
      public name: string = ""	
    }
    

    不写任何修饰符,默认属性和方法的修饰符就是public

  • private 修饰的是仅在同一类中可见、私有的属性或方法

    class Teacher {
      private name: string = ""		// 定义一个私有顺序name,只有在本类中才可以被访问
    
      // 封装了两个对外开放的方法, 通过这两个方法来访问name
      getName() {
        return this.name
      }
    
      setName(newName) {
        this.name = newName
      }
    }
    
  • protected 修饰的是仅在类自身及子类中可见、受保护的属性或方法

    class Person {
      protected name: string = "123"
    }
    
    class Teacher extends Person {
      getName() {
        return this.name
      }
    }
    

    被protected修饰的属性和方法,只有在类内部和子类中可以访问

六、类中的只读属性

如果有一个属性不希望外界可以任意的修改,只希望确定值后直接使用,就可以使用readonly修饰类的属性。

readonly准确的说也是一个修饰符, 使用readonly修饰符修饰的属性是一个只读属性。

class Teacher {
  readonly name: string
  
  constructor(name: string) {
    this.name = name
  }
}

// new 对象的时候就确定了name的值后,就不可以在new 出来的实例当中再去修改了
const teacher = new Teacher("zs")
console.log(teacher.name)
// teacher.name = 'ls' 此时想去这么写代码的话teacher.name就会报错标红

注意点:

只读属性是可以在构造器中赋值, 赋值之后就不可以修改

属性本身不能进行修改, 但是如果它是对象类型, 对象中的属性是可以修改,只要对象的内存地址不改变即可

七、类中的getters/setters

当类中的一些私有属性外界是不能直接访问的,想要监听或者某些属性的获取(getter)和设置(setter)的过程。

这个时候就可以使用访问器的写法。

class  Teacher {
  private _name: string
  constructor(name: string) {
    this._name = name
  }

  // 访问器的写法
  // setter
  set name(newName) {
    this._name = newName
  }
  // getter
  get name() {
    return this._name
  }
}

// 访问器调用方式
const p = new Teacher("zs")
p.name = "ls"				// 虽然访问器叫做name(),但是调用的时候还是像调用属性一样去调用
console.log(p.name)

相比于直接写get/set方法的方式去暴露私有属性,更加推荐写访问器的方式去露私有属性

八、类中的静态成员

一般来说,在类中定义的成员和方法都属于对象级别的,它们属于每一个该类的对象实例。

在开发中, 有时候也需要定义类级别的成员和方法。 意思就是该成员变量属于类,而不属于该类的对象实例。

TypeScript中通过关键字static来定义一个静态成员。

class Student {
  static time: string = "09:00"

  static attendClass() {
    console.log("去学习typescript~")
  }
}

console.log(Student.time)	// 直接通过类名去调用静态成员变量
Student.attendClass()		// 直接通过类名去调用静态成员方法

静态成员的最大特点就是属于类而不属于类的实例

静态成员需要用类名直接去调用

九、抽象类

9.1 抽象方法的概念

继承是多态使用的前提。 所以在定义通用接口时, 通常会把调用者传入父类,通过多态实现更加灵活的调用方式。

但是,父类本身可能并不需要对某些方法进行具体的实现,所以父类中定义的方法,,我们可以定义为抽象方法。

TypeScript中没有具体实现的方法(没有方法体),就是抽象方法。

9.2 抽象类的概念

具有抽象方法的类就是抽象类。

9.3 抽象类的特点

  • 抽象方法,必须存在于抽象类中

  • 抽象类使用abstract声明

  • 抽象类是不能被实例化(即不能通过new创建对象)

  • 抽象方法必须被子类实现,否则该子类必须是一个抽象类, 如果是一个抽象类就可以不实现;

9.4 代码示例

// 定义一个形状的抽象类
abstract class Shape {
  abstract getArea(): number
}

// 定义正方形类(继承shape类并且实现shape类中的抽象方法)
class Square extends Shape {
  private width: number
  private height: number

  constructor(width: number, height: number) {
    super()
    this.width = width
    this.height = height
  }

  getArea() {
    return this.width * this.height
  }
}

// 定义圆形形类(继承shape类并且实现shape类中的抽象方法)
class Circle extends Shape {
  private r: number

  constructor(r: number) {
    super()
    this.r = r
  }

  getArea() {
    return this.r * this.r * 3.14
  }
}

// 定义计算面积的类,接收一个Shape类型的参数,所以这个参数应该传入Shape类的实现类的实例
function makeArea(shape: Shape) {
  return shape.getArea()
}

// 测试
const rectangle = new Rectangle(20, 30)
const circle = new Circle(10)

// 根据传入的形状实例去调用对应的getArea方法
console.log(makeArea(rectangle))	// 20*30
console.log(makeArea(circle))		// 10*10*3.14	

十、类的类型

TypeScript中,类本身也可以作为一种数据类型。

10.1 类的实例的类型就是类本身

class Teacher {
  name: string = "zs"
  sleep() { console.log("sleeping...") }
}

const teacher = new Teacher()

此时的teacher变量(对象),它的类型就是Teacher

10.2 直接定义一个变量,指定它的类型为某个类

const teacher: Teacher = {
  name: "ls"
  sleep() { console.log("sleeping...") }
}

注意点就是teacher变量的属性列表要和Teacher类保持一致

10.3 类类型在开发中的使用

function printPerson(t: Teacher) {
  console.log(t.name)
}

printPerson(new Teacher())
printPerson({name: "jack", eating: function() {console.log("sleeping...")}})

一般类类型都是作为函数的参数进行使用

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值