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 阅读(83) 评论(0) 编辑 收藏 举报