阅读 226

TypeScript 中的修饰符

这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战

什么是修饰符?

我们有办法让类中的属性变为只读的吗?

可以啊,在 TS 中只需要给属性加上 readonly 修饰符就可以了。

修饰符,什么是修饰符呢?

修饰符就是一些关键字,可以用来限定类的成员的性质,比如说常见的 public 用来表示共有的属性或者方法,那一共有多少中修饰符呢?

别急,我们一起来系统的学习下类的修饰符吧。

访问控制修饰符

class Cat {
    private name: string;
    
    constructor(name: string) {
        this.name = name;
    }
    // 使用访问控制修饰符
    private sayHi(): string {
        return `Meow, my name is ${this.name}`;
    }
}

let tom = new Cat('Tom');
tom.name = 'Jack'; // 报错:属性“name”为私有属性,只能在类“Cat”中访问
// 如果改成 public 就不报错
// 如果改成 protected 的话,会报错:属性“name”受保护,只能在类“Cat”及其子类中访问

// 报错:属性“sayHi”为私有属性,只能在类“Cat”中访问
// 如果改成 public 就不报错
// 如果改成 protected 的话,会报错:属性“sayHi”受保护,只能在类“Cat”及其子类中访问
tom.sayHi();
复制代码

区别

当前类子类实例
private
protected
public

习题:关于访问控制修饰符,下面用法错误的是?

// A
class Animal {
  public age = 10;
  private getAge() {
    return this.age;
  }
}
new Animal().getAge();

// B
class Animal {
  protected age = 10;
  public getAge() {
    return this.age;
  }
}
new Animal().getAge();

// C
class Animal {
  private age = 10;
  protected getAge() {
    return this.age;
  }
}
new Animal().getAge();

// D
class Animal {
  private age = 10;
  public getAge() {
    return this.age;
  }
}
new Animal().getAge();
复制代码

答案:A C

解析:

private - 被声明的属性(方法)为私有的属性(方法),只能在类 Animal 内部使用。

protected - 被声明的属性(方法)为受保护的属性(方法),即可以在类 Animal 内部使用,也可以在子类中使用。

public - 被声明的属性(方法)为共有的属性(方法),不仅在类 Animal 内部可以使用,也可以在子类和实例中使用。

  • A - age 被修饰为 public,在 getAge 方法中调用用法正确。 getAge 被修饰为 private,在实例 new Animal() 中调用用法错误。
  • B - age 被修饰为 protected,在 getAge 方法中调用用法正确。 getAge 被修饰为 public,在实例 new Animal() 中调用用法正确。
  • C - age 被修饰为 private,在 getAge 方法中调用用法正确。 getAge 被修饰为 protected,在实例 new Animal() 中调用用法错误。
  • D - age 被修饰为 private,在 getAge 方法中调用用法正确。 getAge 被修饰为 public,在实例 new Animal() 中调用用法正确。

只读属性

class Cat {
  private readonly name: string;

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

  changeName(name: string) {
    this.name = name; // 报错:无法分配到“name”,因为它是常数或只读属性
  }
  
  sayHi(): string {
    return `Meow, my name is ${this.name}`;
  }
}

let tom = new Cat('Tom');
tom.changeName('Jack');
复制代码

资料:readonly vs const

在 TypeScript 中,const 是常量标志符,其值不能被重新分配(re-assignment)。

同时 TypeScript 的类型系统同样也允许将 interface、type、 class 上的属性标识为 readonly。

interface IUser {
    readonly name: string
}

type User = {
    readonly name: string
}

class Person {
    readonly name = 'Rich'
}
复制代码

readonly 和 const 看起来做了同样的事情。但是他们其实并不相同,我们可以从这几个方面来区别:

  • readonly 实际上只是在编译阶段进行代码检查。而 const 则会在运行时检查(在支持 const 语法的 JavaScript 运行时环境中)。
  • readonly 是属性,而 const 是常量

习题:关于只读属性,下面用法错误的是?

// A
class Animal {
  private readonly age = 10;
  public getAge() {
    return this.age;
  }
}
new Animal().getAge();

// B
class Animal {
  public readonly age = 10;
  public getAge() {
    return this.age;
  }
}
new Animal().age;

// C
class Animal {
  readonly age: number = 10;
  constructor(age: number) {
      this.age = age;
  }
}
new Animal(20).age;

// D
class Animal {
  readonly age: number = 10;
  setAge(age: number) {
    this.age = age;
  }
}
new Animal().setAge(20);
复制代码

答案:D

解析:

readonly 只能用于修饰属性,不能用于修饰方法。被 readonly 修饰的属性只能初始化阶段赋值和 constructor 中赋值。其他任何方式的赋值都是不被允许的。

  • A - age 被修饰为 private readonly,在 getAge 方法中调用且未发生赋值操作用法正确。 getAge 被修饰为 public,在实例中调用用法正确。
  • B - age 被修饰为 public readonly,未发生赋值操作用法正确。
  • C - age 被修饰为 readonly,在初始化阶段赋值和 constructor 中赋值用法正确。
  • D - age 被修饰为 readonly,在 setAge 方法中赋值用法错误。

静态属性

class Cat {
  // 猫能活的最长寿命
  static maxAge = 38;
  static setMaxAge(age: number) {
    Cat.maxAge = age;
  }

  private name: string;

  constructor(name: string) {
    this.name = name;
  }
  
  sayHi(): string {
    return `Meow, my name is ${this.name}`;
  }
}

let tom = new Cat('Tom');
Cat.maxAge = 40;
Cat.setMaxAge(50);
复制代码

习题:关于静态属性,下面用法正确的是?

// A
class Animal {
  public static age = 10;
  public static getAge() {
    return this.age;
  }
}
Animal.getAge();

// B
class Animal {
  public age = 10;
  public static getAge() {
    return this.age;
  }
}
Animal.getAge();

// C
class Animal {
  public static age = 10;
  public getAge() {
    return this.age;
  }
}
new Animal.getAge();

// D
class Animal {
  public static age = 10;
  public static getAge() {
    return Animal.age;
  }
}
Animal.getAge();
复制代码

答案:A D

解析:

学习完属性和方法,我们知道类的属性默认是实例属性,类的方法默认是实例方法。

TypeScript 中提供了访问修饰符 static 用于定义类的静态属性和方法。静态属性和方法可直接通过类名访问,静态方法中只能访问静态属性和静态方法。

  • A - getAge 被修饰为 public staticAnimal.getAge() 调用是正确的。age 被修饰为 public static,在静态方法 getAge 中调用静态属性 age 是正确的,故本选项正确。
  • B - getAge 被修饰为 public staticAnimal.getAge() 调用是正确的。age 被修饰为 public,在静态方法 getAge 中调用实例属性 age 是错误的。故本选项错误。
  • C - getAge 被修饰为 publicnew Animal().getAge() 调用是正确的。age 被修饰为 public static,在实例方法 getAge 中使用 this.age 是错误的。故本选项错误
  • D - getAge 被修饰为 public staticAnimal.getAge() 调用是正确的。age 被修饰为 public static,在静态方法 getAge 中调用静态属性 age 是正确的,故本选项正确。

抽象类

abstract class Animal {
  name: string;
  /* abstract sayHi(): string { // 报错:方法“sayHi”不能具有实现,因为它标记为抽象
    return "Hi";
  } */
  abstract sayHi(): string
}

let animal = new Animal(); // 报错:无法创建抽象类的实例。

class Cat extends Animal {
  sayHi() {
    return `Meow, my name is ${this.name}`;
  }
}

class Dog extends Animal { // 报错:非抽象类“Dog”不会实现继承自“Animal”类的抽象成员“sayHi”
  sayHi() {
    return "Hi";
  }
}
复制代码

修饰符总结

名称描述
访问控制修饰符private,protected,public
只读属性readonly
静态属性static
抽象类、抽象方法abstract

习题:关于抽象类,下面用法正确的是?

// A
abstract class Animal {
  public age = 10;
  public getAge() {
    return this.age;
  }
}
new Animal().getAge(); // 报错:无法创建抽象类的实例

// B
abstract class Animal {
  public age = 10;
  public abstract getAge(): number;
}
class Panda extends Animal {
  public getAge() { return this.age; }
}
new Panda().getAge();

// C
abstract class Animal {
  public age = 10;
  public abstract getAge(): number;
}
class Panda extends Animal { // 报错:非抽象类“Panda”不会实现继承自“Animal”类的抽象成员“getAge”
  public setAge(age: number) {
    return this.age = age;
  }
}
new Panda().setAge(20);

// D
abstract class Animal {
  public age = 10;
  public abstract setAge(age: number): void;
}
class Panda extends Animal { // 报错
  public setAge(age: string) {
    this.age = age;
  }
}
new Panda().setAge('20');
/* 
报错:
类型“Panda”中的属性“setAge”不可分配给基类型“Animal”中的同一属性。
  不能将类型“(age: string) => void”分配给类型“(age: number) => void”。
    参数“age”和“age” 的类型不兼容。
      不能将类型“number”分配给类型“string”。
*/
复制代码

答案:B

解析:

abstract 修饰的类为抽象类,在抽象类中被 abstract 修饰的方法为抽象方法。抽象类不能被实例化,抽象类中的抽象方法必须被子类实现。本例中使用 extends 继承了父类 Animal(下章节介绍类继承相关知识)

选项中的 Animal 为抽象类

  • A - 抽象类不能被实例化,故错误。
  • B - 子类 Panda 继承于抽象类 Animal 并实现了抽象方法 getAge,用法正确。
  • C - 子类 Panda 继承于抽象类 Animal,但是未实现抽象方法 getAge,故错误。
  • D - 抽象类 Animal 中的抽象方法 setAge 的函数类型为 public setAge(age: number): void,而子类实现的 setAge 的函数类型为 public setAge(age: string): void,与抽象类所定义的函数类型不相符,故错误。
文章分类
前端
文章标签