类定义

  • class Person {} — 类的声明
  • const Animal = class {}; — 类的表达式
  1. 类的声明不像函数一样可以声明提升
  2. 函数受函数作用域限制,类受块级作用域限制

类的构成

  • 构造函数方法 constructor() {}
  • 实例方法 class Foo {}
  • 获取函数 设置函数 get baz() set baz()
  • 静态类方法 static baz() {}

类构造函数

  • constructor 会在new时调用

    实例化

  • new会实施一下几个步骤

  1. 在内存中创建一个新对象
  2. 在这个新对象的内部[[Prototype]]指针被赋值为构造函数的prototype属性
  3. 构造函数内部的this被赋值为这个新对象(即this指向的这个对象)
  4. 执行构造函数内部的代码(给新对象添加属性)
  5. 如果构造函数返回非空对象,则返回该对象;否则返回刚刚创建的对象。 如果返回的是普通对象,instanceof失效
  • 实例化时传入的参数,会当做constructor的参数

    1. class Person {
    2. constructor(name) {
    3. console.log(arguments.length);
    4. this.name = name || null;
    5. }
    6. }
  • 类在不用new实例时,会报错

  • 类可以立即实例化

    1. let p = new class Foo {
    2. constructor(x) {
    3. console.log(x);
    4. }
    5. }('bar'); // bar

    实例成员

  • 所有成员是独立的

  • 定义的方法,都是在原型上的,可以用变量定义方法名
  • 也支持 set 和get访问器
  • 静态方法定义在 类本身,在调用的时候直接调用,调用的时候,非常使用实例工厂

    1. class Person {
    2. constructor(age) {
    3. this.age_ = age;
    4. }
    5. sayAge() {
    6. console.log(this.age_);
    7. }
    8. static create() {
    9. // 使用随机年龄创建并返回一个 Person 实例
    10. return new Person(Math.floor(Math.random()*100));
    11. }
    12. }
    13. console.log(Person.create()); // Person { age_: ... }
  • 不能直接定义在原型上的属性,但是可以通过手动添加 Person.prototype.name = ‘Jake’;

  • 可以使用迭代器和生成器方法

    1. class Person {
    2. // 在原型上定义生成器方法
    3. *createNicknameIterator() {
    4. yield 'Jack';
    5. yield 'Jake';
    6. yield 'J-Dog';
    7. }
    8. // 在类上定义生成器方法
    9. static *createJobIterator() {
    10. yield 'Butcher';
    11. yield 'Baker';
    12. yield 'Candlestick maker';
    13. }
    14. }
    15. let jobIter = Person.createJobIterator();
    16. console.log(jobIter.next().value); // Butcher
    17. console.log(jobIter.next().value); // Baker
    18. console.log(jobIter.next().value); // Candlestick maker
    19. let p = new Person();
    20. let nicknameIter = p.createNicknameIterator();
    21. console.log(nicknameIter.next().value); // Jack
    22. console.log(nicknameIter.next().value); // Jake
    23. console.log(nicknameIter.next().value); // J-Dog

    继承

    虽然类继承使用的是新语法,但背后依旧使用的是原型链。

  • 使用extends关键字,就可以拥有父亲身上的属性和原型对象

    1. class Vehicle {}
    2. // 继承类
    3. class Bus extends Vehicle {}
    4. let b = new Bus();
    5. console.log(b instanceof Bus); // true
    6. console.log(b instanceof Vehicle); // true
    7. function Person() {}
    8. // 继承普通构造函数
    9. class Engineer extends Person {}
    10. let e = new Engineer();
    11. console.log(e instanceof Engineer); // true
    12. console.log(e instanceof Person); // true

    super关键字

  • 子类方法可以通过super关键字引用他们的原型 — super只用用于子类中

  1. 在静态方法中可以通过 super 调用继承的类上定义的静态方法
  2. super 只能在派生类构造函数和静态方法中使用
  3. 不能单独引用 super 关键字,要么用它调用构造函数,要么用它引用静态方法
  4. 调用 super()会调用父类构造函数,并将返回的实例赋值给 this
  5. super()的行为如同调用构造函数,如果需要给父类构造函数传参,则需要手动传入
  6. 如果没有定义类构造函数,在实例化派生类时会调用 super(),而且会传入所有传给派生类的 参数
  7. 在类构造函数中,不能在调用 super()之前引用 this
  8. 如果在派生类中显式定义了构造函数,则要么必须在其中调用 super(),要么必须在其中返回 一个对象

抽象类

  1. // 抽象基类
  2. class Vehicle {
  3. constructor() {
  4. if (new.target === Vehicle) {
  5. throw new Error('Vehicle cannot be directly instantiated');
  6. }
  7. // 用this判断是否是抽象类,进行了new调用,可以用ts
  8. if (!this.foo) {
  9. throw new Error('Inheriting class must define foo()');
  10. }
  11. console.log('success!');
  12. }
  13. }

扩展原始类型

  • 实例的类型与原始实例的类型是一致的

    1. class SuperArray extends Array {}
    2. let a1 = new SuperArray(1, 2, 3, 4, 5);
    3. let a2 = a1.filter(x => !!(x%2))
    4. console.log(a1); // [1, 2, 3, 4, 5]
    5. console.log(a2); // [1, 3, 5]
    6. console.log(a1 instanceof SuperArray); // true
    7. console.log(a2 instanceof SuperArray); // true
  • 可以覆盖 Symbol.species 访问器

类混入

  1. class Vehicle {}
  2. let FooMixin = (Superclass) => class extends Superclass {
  3. foo() {
  4. console.log('foo');
  5. }
  6. };
  7. let BarMixin = (Superclass) => class extends Superclass {
  8. bar() {
  9. console.log('bar');
  10. }
  11. };
  12. let BazMixin = (Superclass) => class extends Superclass {
  13. baz() {
  14. console.log('baz');
  15. }
  16. };
  17. function mix(BaseClass, ...Mixins) {
  18. return Mixins.reduce((accumulator, current) => current(accumulator), BaseClass);
  19. }
  20. class Bus extends mix(Vehicle, FooMixin, BarMixin, BazMixin) {}
  21. let b = new Bus();
  22. b.foo(); // foo
  23. b.bar(); // bar
  24. b.baz(); // baz
  • 很多 JavaScript 框架(特别是 React)已经抛弃混入模式,转向了组合模式