10.【TypeScript 教程】类(Class)

简介: 10.【TypeScript 教程】类(Class)

自 ES6 起,终于迎来了 class,对于开发者来说,终于可以使用基于类的面向对象式编程。TypeScript 在原 ES6 中类的基础上,还添加了一些新的功能,比如几种访问修饰符,这是在其他面向对象语言中早就实现了的。


JavaScript 的类作为语法糖,我们不但需要知道怎么去使用,还应该了解其本质,涉及到原型的部分希望大家能深入理解。

1. 解释

类描述了所创建的对象共同的属性和方法。通过 class 关键字声明一个类,主要包含以下模块:

  • 属性
  • 构造函数
  • 方法

2. 类的本质

JavaScript 中,生成实例对象可以通过构造函数的方式:

实例演示

function Calculate (x, y) {
  this.x = x
  this.y = y
}
 
Calculate.prototype.add = function () {
  return this.x + this.y
}
 
var calculate = new Calculate(1, 2)
console.log(calculate.add()) // 3

如果通过 class 关键字进行改写:

实例演示

class Calculate {
  // 类的属性
  public x: number
  public y: number
 
  // 构造函数
  constructor(x: number, y: number) {
    this.x = x
    this.y = y
  }
 
  // 类的方法
  add () {
    return this.x + this.y
  }
}
 
const calculate = new Calculate(1, 2)
console.log(calculate.add()) // 3
 
console.log(typeof Calculate) // 'function'
console.log(Calculate === Calculate.prototype.constructor) // true

代码解释:

最后一行,可以看出,类指向其构造函数本身,class 关键字可以看做是一个语法糖。

constructor() 方法是类的默认方法,通过 new 来生成对象实例时,自动调用该方法。换句话说,constructor() 方法默认返回实例对象 this


3. 类的继承

基于类的程序设计中一种最基本的模式是允许使用继承来扩展现有的类,这样可以抽出公共部分让子类复用。

使用 extends 关键字来实现继承:

实例演示

// 继承 JavaScript 内置的 Date 对象
class LinDate extends Date {
 
  getFormattedDate() {
    var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
    return this.getDate() + "-" + months[this.getMonth()] + "-" + this.getFullYear();
  }
}
 
const date = new LinDate()
 
console.log(date.getFullYear());     // 2024
console.log(date.getFormattedDate()) // 11-Jan-2024

代码解释: LinDate 继承了 Date 的功能,可以使用父类 Date 的方法 getFullYear(),也可以使用自身的方法 getFormattedDate()。


子类在 constructor 内中使用 super() 方法调用父类的构造函数,在一般方法内使用 super.method() 执行父类的方法。

实例演示

class Animal {
  public name:string
 
  constructor(name: string) { 
    this.name = name 
  }
 
  move(distance: number = 0) {
      console.log(`${this.name} moved ${distance}m.`)
  }
}
 
class Dog extends Animal {
  constructor(name: string) { 
    // 调用父类的构造函数
    super(name)
  }
 
  move(distance = 10) {
      console.log('bark...')
      // 执行父类的方法
      super.move(distance) 
  }
}
 
const dog: Animal = new Dog('Coco')
 
dog.move() // Coco moved 10m.

代码解释:

第 16 行,通过 super() 调用了父类的构造函数。

第 22 行,通过 super 关键字调用父类的方法。

4. 访问修饰符

TypeScript 可以使用四种访问修饰符 public、protected、private 和 readonly。



4.1 public

TypeScript 中,类的成员全部默认为 public,当然你也可以显式的将一个成员标记为 public,标记为 public 后,在程序类的外部可以访问。

class Calculate {
  // 类的属性
  public x: number
  public y: number
 
  // 构造函数
  public constructor(x: number, y: number) {
    this.x = x
    this.y = y
  }
 
  public add () {
    return this.x + this.y
  }
}

4.2 protected

当成员被定义为 protected 后,只能被类的内部以及类的子类访问

class Base {
  protected baseUrl: string = 'http://api.com/'
 
  constructor() {}
 
  protected request(method: string) {
    const url = `${this.baseUrl}${method}`
    // TODO 封装基础的 http 请求
  }
}
 
class Address extends Base {
  get() {
    return this.request('address')
  }
}

代码解释:

第 2 行,Base 类的属性 baseUrl 被定义为受保护的,那么第 7 行该属性在类中被访问是可以的。

第 14 行,因 Address 类是 Base 类的子类,在子类中允许访问父类中被定义为受保护类型的方法 request() 。

4.3 private

当类的成员被定义为 private 后,只能被类的内部访问

class Mom {
  private labour() {
    return 'baby is coming'
  }
}
 
class Son extends Mom {
  test () {
    this.labour() // Error, Property 'labour' is private and only accessible within class 'Mom'
  }
}

代码解释:

第 9 行,父类中的 labour() 方法被定义为私有方法,只能在父类中被使用,子类中调用报错。

4.4 readonly

通过 readonly 关键字将属性设置为只读的。只读属性必须在声明时或构造函数里被初始化。

class Token {
  readonly secret: string = 'xjx*xh3GzW#3'
 
  readonly expired: number
 
  constructor (expired: number) {
    this.expired = expired
  } 
}
 
const token = new Token(60 * 60 * 24)
token.expired = 60 * 60 * 2 // Error, expired 是只读的

代码解释:

最后一行,因 Token 类的属性 expired 被设置为只读属性,不可被修改。

5. 静态方法

通过 static 关键字来创建类的静态成员,这些属性存在于类本身上面而不是类的实例上

class User {
  static getInformation () {
    return 'This guy is too lazy to write anything.'
  }
}
 
User.getInformation() // OK
 
const user = new User()
user.getInformation() // Error 实例中无此方法

代码解释: getInformation() 方法被定义为静态方法,只存在于类本身上,类的实例无法访问。

静态方法调用同一个类中的其他静态方法,可使用 this 关键字。

class StaticMethodCall {
 
  static staticMethod() {
      return 'Static method has been called'
  }
  static anotherStaticMethod() {
      return this.staticMethod() + ' from another static method'
  }
 
}

代码解释: 静态方法中的 this 指向类本身,而静态方法也存在于类本身,所以可以在静态方法中用 this 访问在同一类中的其他静态方法。

非静态方法中,不能直接使用 this 关键字来访问静态方法。而要用类本身或者构造函数的属性来调用该方法:

class StaticMethodCall {
  constructor() {
      // 类本身调用
      console.log(StaticMethodCall.staticMethod())
 
      // 构造函数的属性调用
      console.log(this.constructor.staticMethod())
  }
  static staticMethod() {
      return 'static method has been called.'
  }
}

代码解释: 类指向其构造函数本身,在非静态方法中,this.constructor === StaticMethodCalltrue, 也就是说这两种写法等价。

6. 抽象类

抽象类作为其它派生类的基类使用,它们一般不会直接被实例化,不同于接口,抽象类可以包含成员的实现细节。

abstract 关键字是用于定义抽象类和在抽象类内部定义抽象方法。

abstract class Animal {
    abstract makeSound(): void;
    move(): void {
        console.log('roaming the earch...');
    }
}
 
const animal = new Animal() // Error, 无法创建抽象类实例

通常我们需要创建子类继承抽象类,将抽象类中的抽象方法一一实现,这样在大型项目中可以很好的约束子类的实现。

class Dog extends Animal {
  makeSound() {
    console.log('bark bark bark...')
  }
}
 
const dog = new Dog()
 
dog.makeSound()  // bark bark bark...
dog.move()       // roaming the earch...

7. 把类当做接口使用

类也可以作为接口来使用,这在项目中是很常见的。

class Pizza {
  constructor(public name: string, public toppings: string[]) {}
}
 
class PizzaMaker {
  // 把 Pizza 类当做接口
  static create(event: Pizza) {
    return new Pizza(event.name, event.toppings)
  }
}
 
const pizza = PizzaMaker.create({ 
  name: 'Cheese and nut pizza', 
  toppings: ['pasta', 'eggs', 'milk', 'cheese']
})

第 7 行,把 Pizza 类当做接口。


因为接口和类都定义了对象的结构,在某些情况下可以互换使用。如果你需要创建一个可以自定义参数的实例,同时也可以进行类型检查,把类当做接口使用不失为一个很好的方法。


这就是 TypeScript 的强大功能,而且非常灵活,拥有全面的面向对象设计和通用的类型检查。

8. 小结

本节介绍了类的本质及其使用方法,需要注意:

  • 类指向其构造函数本身。
  • 静态方法存在于类本身上面而不是类的实例上。
  • 抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。
  • TypeScript 新增了 public、protected、private 等访问修饰符。
  • 子类继承父类时,在其构造函数 constructor() 中不要忘了 super() 方法。
相关文章
|
2月前
|
JavaScript 前端开发
TypeScript【类型别名、泛型】超简洁教程!再也不用看臭又长的TypeScript文档了!
【10月更文挑战第11天】TypeScript【类型别名、泛型】超简洁教程!再也不用看臭又长的TypeScript文档了!
|
2月前
|
JavaScript 前端开发 Java
TypeScript【接口】超简洁教程!再也不用看臭又长的TypeScript文档了!
【10月更文挑战第10天】TypeScript【接口】超简洁教程!再也不用看臭又长的TypeScript文档了!
|
2月前
|
JavaScript 前端开发
Vue2整合TypeScript:借助vue-property-decorator以类模式编写组件
Vue2整合TypeScript:借助vue-property-decorator以类模式编写组件
145 3
|
2月前
|
JavaScript 前端开发 安全
TypeScript【基础类型】超简洁教程!再也不用看臭又长的TypeScript文档了!
【10月更文挑战第9天】TypeScript【基础类型】超简洁教程!再也不用看臭又长的TypeScript文档了!
|
2月前
|
JavaScript 索引
TypeScript(TS)安装指南与基础教程学习全攻略(二)
TypeScript(TS)安装指南与基础教程学习全攻略(二)
56 0
|
2月前
|
JavaScript 前端开发 安全
TypeScript(TS)安装指南与基础教程学习全攻略(一)
TypeScript(TS)安装指南与基础教程学习全攻略(一)
29 0
|
3月前
|
JavaScript
typeScript进阶(13)_类与注意事项(八项特性)
TypeScript的类支持特性包括:构造函数、继承(使用`extends`)、公有/私有/受保护修饰符、只读修饰符、参数属性、存取器(getters/setters)、抽象类(用`abstract`声明)。类可用作类型。
24 0
typeScript进阶(13)_类与注意事项(八项特性)
|
4月前
|
JavaScript 前端开发 编译器
TypeScript教程(一)在vscode中的配置TypeScript环境
本文是一篇TypeScript入门教程,介绍了在VS Code中配置TypeScript环境的步骤,包括安装Node.js、使用npm安装TypeScript、配置npm镜像源、安装VS Code的TypeScript扩展,以及创建和运行一个简单的TypeScript "Hello World"程序。
TypeScript教程(一)在vscode中的配置TypeScript环境
|
4月前
|
资源调度 JavaScript 前端开发
TypeScript实战教程(一):表单上传与后端处理
本文是TypeScript实战教程的第一部分,介绍了使用TypeScript进行表单上传和后端处理的完整流程,包括环境配置、前端表单创建、使用TypeScript和Express框架搭建服务端、处理表单数据,并提供了详细的代码示例和运行测试方法。
TypeScript实战教程(一):表单上传与后端处理
|
5月前
|
JavaScript 前端开发 程序员
Typescript 【实用教程】(2024最新版)含类型声明,类型断言,函数,接口,泛型等
Typescript 【实用教程】(2024最新版)含类型声明,类型断言,函数,接口,泛型等
81 0