TypeScript----Class(上)

说明:本文章少部分内容摘取于阮一峰教授的 ES6

🔯 Class

JS 在没有 Class 之前,生成实例的常用方法是通过构造函数:

function Point(x, y) {
  this.x = x;
  this.y = y;               
}

Point.prototype.toString = function() {
  return (`the point of x is ${this.x} and y is ${this.y}`)
}

const p = new Point(2.4, 6.8);

上面的写法虽然是一种继承,但是和传统的继承在写法上有很大差异,我们用 ES6 改写一下:

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
   return `...${this.x}...${this.y}`
  }
}

使用这种方式语法上更接近其他编程语言,可读性也好了很多。

生成一个实例 class 和 function 别无两样,都是通过 new 关键字创建的。

注意:class 实质上是一个语法糖,并不存在真正的 Class 类型,这里的 class Point 等价于 Point.prototype 。

 

🔯 Constructor 方法和实例属性

constructor() 是 class 默认的方法,生成实例时自动调用。如果没有显式的定义这个方法,class 会提供一个 constructor()。

class Point {}

// 等同于
class Point {
  constructor() {}
}

constructor 方法一般用于初始化属性,继承父级甚至指向其他对象。

class IncreasingCounter {
  constructor() {
   this._count = 0;
   // 属性初始化
  }

  get value() {
    console.log('Getting this current value!')
    return this._count;
  }

  increment() {
    this._count++;
  }
}

实例属性 _count 除了像上面一样写在 constructor 中,还可以简写在类的最顶层

class IncreasingCounter {
  _count = 0;
  
  get value() {}
  increment() {}
}

 

🔯 取值函数 getter 和 存值函数 setter

取值函数和存值函数都是对某个属性一种拦截。

class ClassName {
  constructor() {}

  get propName() {
    return 'always me';
  }
  set propName(value) {
    console.log('I can't modify the propName to', value);
  }
}

const inst = new ClassName();
inst.propName = 'Gryffindor' 
// I can't modify the propName to Gryffindor inst.propName // always me

 

🔯 静态方法

在一个方法前加上 static 关键字,则表示该方法不会被实例继承,而是直接通过类调用。

class HaveStaticClass {
  static classMethod() {
    return 'I am a Static Method'
  }
}

HaveStaticClass.classMethod() 
// 'I am a Static Method'

const Inst = new HaveStaticClass();
Inst.classMethod();
// Error: Inst.classMethod is not a function

注意:静态方法的 this 指向的是 类,不会指向实例

 

🔯 继承 extends 

子类继承父类的属性和方法

class Outter{}
class Inner extends Outter {}

子类在继承了父类以后,必须先调用 super() ,否则this指向不明,导致无法生成实例。

class Outter {}
class Inner extends Outter {
  constructor() {}
}

const Inst = new Inner();
//Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor

解决上面的问题,可以在子类中忽略 constructor 方法,子类会默认执行一次 constructor 方法和 super 方法

class Outter {}
class Inner extends Outter {
  
}

const Inst = new Inner();

// Or

class Outter {}
class Inner extends Outter {
  constructor() {
      super();
  }
}

const Inst = new Inner();

为什么子类的构造函数,一定要调用super()?原因就在于 ES6 的继承机制,与 ES5 完全不同。ES5 的继承机制,是先创造一个独立的子类的实例对象,然后再将父类的方法添加到这个对象上面,即“实例在前,继承在后”。ES6 的继承机制,则是先将父类的属性和方法,加到一个空的对象上面,然后再将该对象作为子类的实例,即“继承在前,实例在后”。这就是为什么 ES6 的继承必须先调用super()方法,因为这一步会生成一个继承父类的this对象,没有这一步就无法继承父类

 

下面切入正题,Class 在 TS 中的语法

🔯 实例属性的类型声明

class Point {
  x: number;
  y: number;
}

const pt = new Point();
pt.x = 100;
pt.y = 200;

// 你也可以通过字面量的形式,初始化并赋予属性的类型

class Point {
  x = 0;
  y = 0;
}

🔯 readonly

实例属性前可以跟一个 readonly 修饰符,表示这个属性只能在类的 constructor 中修改,其他位置都不可以修改,注意:readonly 只用于属性,不能用于方法

class Greeter {
  readonly name: string = 'WORLD';
  constructor(otherName?: string) {
    if(otherName !== undefind) {
      this.name = otherName;
    }
  }

  err() {
   this.name = 'this is not OK'
   // Cannot assign to 'name' because it is a read-only property.
  }
}

const inst = new Greeter();
inst.name = 'HaH';
// Cannot assign to 'name' because it is a read-only property.

🔯 方法

在TS 中声明一个类的方法和普通函数没有两样

class Point {
  x = 10;
  y = 100;

  scale(n: number): void {
    this.x *= n;
    this.y *= n;
  }
}

🔯 Getters / Setters

 TS 中,getter, setter 需要注意以下三点:

  • 如果只有get, 没有 set, 那么这个属性自动变为 readonly;
  • 如果 set 的参数没有指定类型,set 参数的类型指向 get 返回值的类型;
  • get 和 set 必须是可见成员,后面会讲到的, public 成员
class Thing {
  _size = 0;

  get size(): number {
     return this._size;
  }

  set size(value: string | number | boolean) {
    let num = Number(value);

    // Don't allow NaN, Infinity, etc

    if(!Number.isFinite(num)) {
      this._size = 0;
      return;
    }

    this._size = num;
  }
}

 🔯  implements 

你可以使用一个 implements 关键字,将 class 关联到一个或者多个 interface ,使其符合 interface 定义的类型。

interface Pingable {
  ping(): void;
}

interface Other {
  other(a: number ): number;
}

class Sonar implements Pingable {
  ping() {
    console.log("ping!");
  }
}

class Ball implements Pingable {
  // Class 'Ball' incorrectly implements interface 'Pingable'.
  // Property 'ping' is missing in type 'Ball' but required in type 'Pingable'.

  pong() {
    console.log("pong!");
  }
}
// 关联多个 interface class Multi implements Pingable, Other { ping() { } other(a: number ) {
return a + 10 } }

注意:implements 只是让 class 可以接受 interface 定义的类型,并不会改变 class 本身和方法的类型,下面的例子中
check 方法中的参数 s 并不会因为 Checkable 而变成 string 类型

interface Checkable {
  check(name: string): boolean;
}

class NameChecker implements Checkable {
  check(s: any) {

    // Notice no error here
    return s.toLowercse() === "ok";
    //         ^?
  }
}

当然了 implements 指向的接口也可以像普通类型一样 支持可选

interface A {
  x: number;
  y?: number;
}
class C implements A {
  x = 0;
}
const c = new C();
c.y = 10;
// Property 'y' does not exist on type 'C'.

 

posted on 2022-03-13 20:38  Deflect-o-Bot  阅读(76)  评论(0编辑  收藏  举报