在传统的 JavaScript 语法中,使用构造函数来实现类,使用原型链实现继承关系。在 ES6 之后终于迎来了 class (只是一个优化的语法糖,本质仍是原型链)。

1 类的概念

  • 类 ( Class ) :定义了一类事物的抽象特征,包括其属性和行为(方法);
  • 对象 ( Object ) :类的实例化,通过 new 关键字创建;
  • 面向对象 ( OOP ) :描述类与对象的关系,一个类对应多个实例,一个实例只能对应一个类,特点总结,封装、继承、多态;
    • 封装:将类/对象的某些特征隐藏,只暴露提供其访问方法,避免直接操作该特征数据;
    • 继承:类与类之间可以产生继承关系,子类继承父类后,子类不仅能拥有父类的(所有)特性,还能拥有自己的特性;
    • 多态:多个子类继承同一父类,可以对父类的特征进行不同的重载实现;如定义动物类用于移动方法,鸟类和狗类都继承于动物类,但鸟类的移动方法为飞,狗类的移动方法为跑。
  • 存取器 ( getter & setter ) :改变属性的读取和设置行为,于封装类似;
  • 修饰符 ( Modifiers ) :描述特征的性质,对其进行关系限制,如 private,protected,public,static
  • 抽象类 ( Abstract Class ) :抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现;
  • 接口 ( Interfaces ) :不同类之间公有的属性或方法,可以抽象成一个接口。接口可以被类实现 implements 。一个类只能继承自另一个类,但是可以实现多个接口;

    2 类的用法

    2.1 ES6

    ```javascript // 定义 class Animal { // 属性 public name; // 构造函数 constructor(name) { this.name = name; } // 方法 public move() { return this.name + ‘ is moving!’; } // 静态方法 static isAnimal(a) { return a instanceof Animal; } } // 实例对象 let animal = new Animal(‘Monster’); console.log(animal.move()); // ‘Monster is moving!’ // 继承 class Bird extends Animal { // public name; 可省略 constructor(name) { // 调用父类构造函数 super(name); this.gender = “Female”; } // getter get gender() { return ‘Private’; } // setter set gender(g) { console.log(“Can’t change!”); } // 重载(多态),可省略 public move() { // 调用父类的方法 return super.move() + ‘Flying ~’; } } // 实例对象 let tweety = new Bird(‘Tweety’); console.log(tweety.move()); // ‘Tweety is moving!Flying ~’ console.log(tweety.gender); // ‘Private’ tweety.gender = ‘Male’;

// 调用静态方法 console.log(Animal.isAnimal(tweety)); // true

  1. <a name="be8Y0"></a>
  2. ## 2.2 ES7
  3. ES6 中
  4. - 类的实例属性,需要通过先在类中声明,然后再后构造函数中通过 `this` 初始化赋值;而 ES7 新增了在类中声明实例属性时同时初始化赋值,省略了 `this` ;
  5. - 同时, ES7 新增静态属性,类似于静态方法,使用 `static` 关键字声明,且只能通过类直接访问;
  6. ```typescript
  7. // ES6 实例属性
  8. class Animal {
  9. // 先声明
  10. public name;
  11. contructor(name) {
  12. // 构造函数中,通过 this 初始化
  13. this.name = name;
  14. }
  15. }
  16. // ES7
  17. class Animal {
  18. // 实例属性声明并初始化
  19. public name = "anonymous";
  20. // 静态属性声明并初始化
  21. static kingdom = 'animal';
  22. }

2.3 TS

2.3.1 修饰符

  • TypeScript 中可以使用三种属性修饰符 public, protected, private
    • public 修饰符修饰的属性/方法是公有的,能被子类继承,可以被任意访问;
    • protected 修饰符修饰的属性/方法是受保护的,能被子类继承,仅能由类或子类访问;
    • private 修饰符修饰的属性/方法是私有的,不能被继承,仅能由类访问;
  • ❗注意: protected, private 仅生效于 TypeScript 编译时报错,编译后的 JavaScript 并不存在这两种修饰符,会被当做 public 修饰符,任意访问;
  • 构造函数 constructor 的修饰符:
    • public 修饰构造函数(默认),类可以被继承和实例化;
    • protected 修饰构造函数,类只能被继承,不能实例化;
    • private 修饰构造函数,类不能被继承,也不能实例化;
  • 参数属性:可以将上述修饰符用于构造函数的参数,以简化写法;
    1. class Animal {
    2. constructor(public name: string, protected money: number) {
    3. this.name = name;
    4. this.money = money;
    5. }
    6. }

    2.3.2 只读属性 readonly

    只读属性关键字 readonly ,只允许出现在属性声明索引签名构造函数中。如果它与其他修饰符一起使用时,需要写在修饰符之后:
    class Animal {
    constructor(public readonly name: string, protected money: number) {
      this.name = name;
      this.money = money;
    }
    }
    

    2.3.3 抽象类

    abstract 用于定义抽象类和其中的抽象方法,抽象类不能被 new 关键字实例化,抽象类只能被其它类继承,且继承时必须实现其定义的抽象方法。 ```typescript abstract class Animal { public name: string; // ES7 constructor(name: string) { this.name = name; } public abstract sayHi(): void; // 抽象方法,不能是具体函数声明/表达式 }

// ❗ERROR: 非抽象类“Dog”不会实现继承自“Animal”类的抽象成员“sayHi”。(没有实现抽象方法) class Dog extends Animal { // TS constructor(public name: string) { // 继承抽象类 super(name); this.name = name; } }

class Cat extends Animal { // TS constructor(public name: string) { // 继承抽象类 super(name); this.name = name; } // 实现抽象方法 public sayHi(): void { console.log(“miao~”); } } `` ❗注:EcmaScript 中不存在抽象类的概念,上述 TypeScript 代码编译后,Animal` 类会变为普通类存在,只是删除了抽象方法。

3 类的类型

类的类型限制与接口类似,参考上述代码 或 接口 。