# Class 类

# 类的定义与继承

关于类的定义与继承代码 (opens new window)

// 类里写属性与方法
class Person {
  name = 'toimc'
  getName() {
    return this.name
  }
}
const person = new Person()
console.log(person.getName()) // toimc

// 继承类,继承类属于字类,被继承的属于父类
class Teacher extends Person {
  getTeacherName() {
    return 'toimc Teacher'
  }
  // 子类可以重写父类的属性与方法
  getName() {
    // super 关键字指向了父类,可以直接调用父类。不会受到类重写的影响
    return super.getName() + 'TTT'
  }
}

const teacher = new Teacher()
console.log(teacher.getName()) // toimc
console.log(teacher.getTeacherName()) // toimc Teacher

# 修饰符

访回类型:

  • private:允许在类内使用
  • protected:允许在类内及继承的子类中使用
  • public:允许在类的内外调用(默认)

自带方法:

  • readonly:只读属性
  • static:将方法挂载到类上而不是实例上,实例是没法访问和继承到的

# public

public表示公共的,用来指定在创建实例后可以通过实例访问的,也就是类定义的外部可以访问的属性和方法。

类中的属性默认是 public

class Point {
  public x: number;
  public y: number;
  
  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
  
  public getPosition() {
    return `(${this.x}, ${this.y})`;
  }
}

# private

private修饰符表示私有的,它修饰的属性在类的定义外面是没法访问的:

class Parent {
  private age: number;
  constructor(age: number) {
    this.age = age;
  }
}

const p = new Parent(18);

console.log(p); // { age: 18 }
console.log(p.age); // error 属性“age”为私有属性,只能在类“Parent”中访问
console.log(Parent.age); // error 类型“typeof ParentA”上不存在属性“age”

class Child extends Parent {
  constructor(age: number) {
    super(age);
    console.log(super.age); // 通过 "super" 关键字只能访问基类的公共方法和受保护方法
  }
}

这里在 constructor 中访问 super,这的 super 相当于父类本身,这里我们看到使用 private 修饰的属性,在子类中是没法访问的。

# protected

protected修饰符是受保护修饰符,protected修饰的成员在继承该类的子类中可以访问。

我们再来看下上面那个例子,把父类 Parent 的 age 属性的修饰符 private 替换为 protected:

class Parent {
  protected age: number;
  constructor(age: number) {
    this.age = age;
  }
  protected getAge() {
    return this.age;
  }
}

const p = new Parent(18);

console.log(p.age); // error 属性“age”为私有属性,只能在类“ParentA”中访问
console.log(Parent.age); // error 类型“typeof ParentA”上不存在属性“age”

class Child extends Parent {
  constructor(age: number) {
    super(age);
    console.log(super.age); // undefined
    console.log(super.getAge());
  }
}

new Child(18)

protected还能用来修饰 constructor 构造函数,加了protected修饰符之后,这个类就不能再用来创建实例,只能被子类继承。

需要用new.target来自行判断,而 TS 则只需用 protected 修饰符即可:

class Parent {
  protected constructor() {
    //
  }
}

const p = new Parent(); // error 类“Parent”的构造函数是受保护的,仅可在类声明中访问

class Child extends Parent {
  constructor() {
    super();
  }
}

const c = new Child();

# readonly

在类里可以使用readonly关键字将属性设置为只读。

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

const user = new UserInfo("toimc");

user.name = "haha"; // error Cannot assign to 'name' because it is a read-only property

设置为只读的属性,实例只能读取这个属性值,但不能修改。

# constructor

constructor 构建函数,会在 new 实例的时候自动执行

// 以下两段代码相同, constructor 里,参数前加上public代表在之前已经声明过这个变量了
// 传统写法
class Person {
  public name: string
  constructor(name: string) {
    this.name = name
  }
}
const person = new Person('toimc')
// 简化写法
class Person {
  // public name: string
  constructor(public name: string) {
    // this.name = name
  }
}
const person = new Person('toimc')
console.log(person.name)

字类集成父类并使用 constructor 的话,必须先调用父类的 constructor ,并按照父类的参数规则进行

// super()代表调用父类的 constructor
// 如果父类没有使用constructor 字类需要调用一个空的super()
class Person {
  constructor(public name: string) {}
}

class Teacher extends Person {
  constructor(public age: number) {
    super('toimc')
  }
}

const teacher = new Teacher(28)

# 参数属性

之前的例子中,我们都是在类的定义的顶部初始化实例属性,在 constructor 里接收参数然后对实力属性进行赋值,我们可以使用参数属性来简化这个过程。参数属性简单来说就是在 constructor 构造函数的参数前面加上访问限定符,也就是前面讲的 publicprivateprotectedreadonly 中的任意一个,我们来看例子:

class A {
  constructor(name: string) {}
}

const a = new A("aaa");
console.log(a.name); // error 类型“A”上不存在属性“name”

class B {
  constructor(public name: string) {}
}

const b = new B("bbb");
console.log(b.name); // "bbb"

可以看到,在定义类 B 时,构造函数有一个参数 name,这个 name 使用访问修饰符 public 修饰,此时即为 name 声明了参数属性,也就无需再显示地在类中初始化这个属性了。

# 静态属性

和 ES6 的类一样,在 TS 中一样使用static关键字来指定属性或方法是静态的,实例将不会添加这个静态属性,也不会继承这个静态方法,可以使用修饰符和 static 关键字来指定一个属性或方法:

class Parent {
  public static age: number = 18;
  public static getAge() {
    return Parent.age;
  }
  constructor() {
    //
  }
}

const p = new Parent();
console.log(p.age); // error Property 'age' is a static member of type 'Parent'
console.log(Parent.age); // 18

如果使用了 private 修饰道理和之前的一样:

class Parent {
  public static getAge() {
    return Parent.age;
  }
  private static age: number = 18;
  constructor() {
    //
  }
}

const p = new Parent();
console.log(p.age); // error Property 'age' is a static member of type 'Parent'
console.log(Parent.age); // error 属性“age”为私有属性,只能在类“Parent”中访问。

# 可选类属性

TS 在 2.0 版本,支持可选类属性,也是使用?符号来标记,来看例子:

class Info {
  name: string;
  age?: number;
  constructor(name: string, age?: number, public sex?: string) {
    this.name = name;
    this.age = age;
  }
}

const info1 = new Info("toimc");
const info2 = new Info("toimc", 18);
const info3 = new Info("toimc", 18, "man");