您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

TypeScript 类(Class)

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

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

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

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

function Calculate (x, y) {
  this.x = x
  this.y = y
}

Calculate.prototype.add = function () {
  return this.x + this.y
}

var calculate = new Calculate(, )
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(, )
console.log(calculate.add()) // 3

console.log(typeof Calculate) // 'function'
console.log(Calculate === Calculate.prototype.constructor) // true

解释:

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

constructor() 是类的认,通过 new 来对象实例时,该。换句话说,constructor() 认返回实例对象 this

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

使用 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());     // 2020
console.log(date.getFormattedDate()) // 7-Jan-2020

解释: LinDate 继承了 Date 的,可以使用 DategetFullYear(),也可以使用自身的 getFormattedDate()

子类在 constructor 内中使用 的构造,在一般内使用 super.method() 执行的。

class Animal {
  public name:string

  constructor(name: string) { 
    this.name = name 
  }

  move(distance: number = ) {
      console.log(`${this.name} moved ${distance}m.`)
  }
}

class Dog extends Animal {
  constructor(name: string) { 
    // 的构造
    super(name)
  }

  move(distance = ) {
      console.log('bark...')
      // 执行的
      super.move(distance) 
  }
}

const dog: Animal = new Dog('Coco')

dog.move() // Coco moved 10m.

解释:

第 16 行,通过 了的构造。

第 22 行,通过 super 关键字的。

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

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
  }
}

当成员被定义为 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() 。

当类的成员被定义为 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() 被定义为私有,只能在中被使用,子类中报错。

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

class Token {
  readonly secret: string = 'xjx*xh3GzW#3'

  readonly expired: number

  constructor (expired: number) {
    this.expired = expired
  } 
}

const token = new Token( *  * )
token.expired =  *  *  // Error, expired 是只读的

解释:

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

通过 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, 也就是说这两种写法等价。

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

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...

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

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 的强大,而且非常灵活,拥有全面的面向对象设计和通用的类型检查

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


联系我
置顶