TypeScript----Class (下)

🔯 继承

继承在 TS 中的语法和 JS 没有两样,但是有一点是至关重要的:TS 强制派生类始终是基础类的子类型

class Base {
  getSomething() {
    console.log('this is something ')
  }
}

class Derived extends Base {
  // 覆盖基础类的方法, 携带一个可选的参数
  getSomething(name?: string) {
    if(name === undefined) {
       super.getSomething()
    }else {
      console.log(name.toUpperCase())
    }
  }
}

const Inst = new Derived();
Inst.getSomething();
Inst.getSomething('apple');

派生类遵循基础类,并在实际开发中,通过基类引用派生类是常见的操作,尤其是编写库的时候,如果你读过React 或者 Vue 源码肯定见过这样的写法。

// 接着上一段代码
const OtherInst: Base = Inst;
OtherInst.getSomething()

因为有了继承关系和子类关系,所以当派生类没有遵循基础类时,TS checker 将不会通过:

class Base {
  getSomething() {
    console.log('this is something ')
  }
}

class Derived extends Base {
  // 注意参数 name 是必要参数,这已经违背了基础类的规则
  getSomething(name: string) {
    // Property 'getSomething' in type 'Derived' is not assignable to the same property in base type 'Base'.
    // Type '(name: string) => void' is not assignable to type '() => void'.
  }
}

关于初始化顺序,先基础类后派生类,先初始化后构造。思考下面的执行顺序:

class Base {
  name = "base";
  constructor() {
    console.log("My name is " + this.name);
  }
}
 
class Derived extends Base {
  name = "derived";
}
 
// Prints What ??  base Or derived
const d = new Derived();

 

🔯 类成员的可见性

🔯public 

这是类成员默认的可见性,表示这个成员可以在任何位置被访问到

class Greeter {
  public greet() {
    // 这里的 public 可以省略不写
    console.log('hi')
  }
}

const g = new Greeter();
g.greet();

public 可以省略不写,除非为了保障一定的风格和可读性

 

🔯protected

当类成员使用了 protected 这个关键字,则意味着这个成员只能在派生类中使用,不用用于实例和外部调用。

class Greeter {
  public greet() {
    console.log('Hello,' + this.getName())
  }

  protected getName() {
    // 这里使用了 protected 关键字,所有这个方法只能类和子类内部调用
    return 'hi';
  }
}

class SpecialGreeter extends Greeter {
  public howdy() {
    // 子类内部使用父类的 protected getName 
    console.log('Howdy,' + this.getName());
  }
}

const g = new SpecialGreeter();
g.greet(); // OK
g.getName(); // BAD

但是这种限制不是一定的,在派生类中,可以把基础类中的 protected 成员 变为 public 成员 。

class Base {
  protected m = 10;
  protected printName() {
    console.log(this.m)
  }
}
class Derived extends Base {
  // 默认为 'public'
  m = 15;
  printName() {
    console.log('Change It!')
  }
}
const d = new Derived();
console.log(d.m); // 15
d.printName(); // Change It!

 跨级使用受保护的成员

不同的面向对象的语言,对于是否可以通过基类直接访问受保护的成员各有不同,在 TS 中这样做是不允许的

class Base {
  protected x: number = 1;
}

class Derived1 extends Base {
  protected x: number = 5;
}

class Derived2 extends Base {
  fn1(other: Derived2) {
    other.x = 10; 👍
  }
  fn2(other: Base) {
   other.x = 10; 👎
// Property 'x' is protected and only accessible through an instance of class 'Derived2'. This is an instance of class 'Base'. } }

 

🔯 private

private 类似于 protected,但是通过 private 修饰过的成员只能在基类中访问,派生类和实例都不允许访问。

class Base {
  private x = 0;
}

const inst = new Bsse();
console.log(inst.x)  👎

class Derived extends Base {
  showX() {
    console.log(this.x) 👎
  }
}

 

🔯 static 成员

类可以有自己的静态成员,可以通过类本身调用他们

class MyClass {
  static x = 0;
  static printX() {
    console.log(MyClass.x);
  }
}

MyClass.printX();

static 修饰符也可以和上面提到的可见性修饰符(public, protected, private)一起使用

class MyClass {
  private static x = 0;
}

console.log(MyClass.x)  👎

// Property 'x' is private and only accessible within class 'MyClass'.

 

🔯 静态块

静态块允许编写一系列具有自己作用域的语句,这些语句可以访问包含类中的私有字段。这意味着我们可以使用编写语句的所有功能编写初始化代码,不泄漏变量,并完全访问类的内部。

const loadLastInstances = () =>[]

class Foo {
    static #count = 0;

    get count() {
        return Foo.#count;
    }

    static {
        try {
            const lastInstances = loadLastInstances();
            Foo.#count += lastInstances.length;
        }
        catch {}
    }
}

 

🔯 泛型类

类可以像接口一样具有泛型的定义,使用new实例化泛型类时,其类型参数的推断方式与函数调用中的相同:

class Box<Type> {
  contents: Type;
  constructor(value: Type) {
    this.contents = value;
  }
}

const b = new Box("hello!")

注意:不要在静态成员上使用类型,

class Box<Type> {
  static defaultValue: Type;

  // Static members cannot reference class type parameters.
}

 

🔯 抽象类

可以把基类当作一个抽象类,基类中的方法也可以当作抽象方法,那么这个抽象类就不能直接实例化,而是充当其他派生类的基类。

abstract class Base {
  abstract getName(): string; // 方法的签名

  printName() {
    console.log("Hello, " + this.getName());
  }
}

const b = new Base(); 👎

class Derived extends Base {
  getName() { // 执行方法
    return "world";
  }
}

const d = new Derived(); 👍
d.printName();

 

posted on 2022-03-27 11:34  Deflect-o-Bot  阅读(53)  评论(0编辑  收藏  举报