11 - TS class的补充
类的定义与使用
// 定义一个Point类,拥有x和y两个属性,可以通过构造函数进行初始化,并拥有一个add方法可以进行点的加法操作 class Point { x: number; y: number; // 构造函数,用于初始化对象 constructor(x: number, y: number) { this.x = x; this.y = y; } // 成员函数,用于实现点的加法操作 public add(p: Point): Point { return new Point(p.x + this.x, p.y + this.y); } } // 创建一个Point对象,并调用其add方法 const p = new Point(0, 0); const newP = p.add(new Point(1, 1)); console.log(newP);
成员函数的重载
// Point类,除了基本属性和方法外,增加了成员函数的重载功能 class Point { // ... // 重载的add方法,可以接受两个数字或一个Point对象作为参数 public add(x: number, y: number): Point; public add(p: Point): Point; public add(x: number | Point, y?: number) { if (typeof x === 'number') { return new Point(this.x + x, this.y + y!); } const p = x; return new Point(this.x + p.x, this.y + p.y); } } // 使用重载的add方法 const p = new Point(0, 0); const newP = p.add(new Point(1, 1)); console.log(newP);
Getter和Setter
// C类,展示了getter和setter的使用 class C { _length = 0; // 使用getter获取属性值 get length() { return this._length; } // 使用setter设置属性值 set length(value) { this._length = value; } } // 个人建议的实现方式,使用方法代替getter和setter class C { _length = 0; getLength() { return this._length; } setLength(value) { this._length = value; } }
索引器
// Arr类,展示了索引器的使用 class Arr<T> { [i: number]: T; } const a = new Arr<number>(); a[10] = 100; console.log(a[10]);
类型继承与类的继承
// 接口的继承 interface BasicAddress { name?: string; street: string; city: string; country: string; postalCode: string; } interface AddressWithUnit extends BasicAddress { unit: string; } // 类的继承 class Animal { move() { console.log("Moving along!"); } } class Dog extends Animal { woof(times: number) { for (let i = 0; i < times; i++) { console.log("woof!"); } } } // 使用组合和接口代替继承 interface Movable { move(): void; } class Dog implements Movable { move() { console.log('move dog'); } } // 使用泛型进行继承 class Movable<T extends Animal> { animal: T; move() { console.log(`${this.animal.getName()} is moving`); } }
成员可见域
// 展示public、private和protected三种成员可见域的使用 // 使用public class Point { x: number; } // 使用private class Point { private x: number; constructor() { this.x = x; // 可以在类内部访问 } getX() { return this.x; } } const p = new Point(); // console.log(p.x); // Error: Property 'x' is private and only accessible within class 'Point'.(2341)
关于`protected`关键字
// `protected` 允许在当前类和它的继承类中访问成员。 class Animal { private _name1; // 私有成员,只能在本类中访问 protected _name2; // 受保护的成员,可以在子类中访问 } class Dog extends Animal{ getName1(){ return this._name1 // Error: 不能在子类中访问父类的私有成员 } getName2(){ return this._name2 // 正确: 可以在子类中访问父类的受保护成员 } }
静态成员
class MyClass { static x = 0; // 静态属性 static printX() { // 静态方法 console.log(MyClass.x); // 访问静态属性 } } console.log(MyClass.x); // 访问静态属性 MyClass.printX(); // 调用静态方法
静态成员和类的实例没有关系,静态成员、静态方法都绑定在类型本身上。
静态成员继承
class Base { static getGreeting() { return "Hello world"; // 静态方法返回字符串 } } class Derived extends Base { myGreeting = Derived.getGreeting(); // 子类继承父类的静态方法 }
特殊的静态成员名称
class S { static name = "S!"; // Error : 静态属性'name'与'Function.name'的构造函数内置属性冲突。 }
注意:`name`是Function的一个内置属性,因此在类中定义静态属性`name`会导致错误。
为什么没有静态类?
// 在Java中有静态类,但在JS中,由于类的嵌套和B包的逻辑,逻辑过于复杂,不适合增加嵌套类访问外部类成员的逻辑。 class A { x : number foo(){ const y = 0 class B{ bar(){ this.x // Error: 属性 'x' 在类型 'B' 上不存在 console.log(y) } } return B } }
类型守卫
class FileSystemObject { isFile(): this is FileRep { return this instanceof FileRep; // 判断当前实例是否是FileRep的实例 } isDirectory(): this is Directory { return this instanceof Directory; // 判断当前实例是否是Directory的实例 } isNetworked(): this is Networked & this { return this.networked; // 判断当前实例是否有networked属性 } constructor(public path: string, private networked: boolean) {} } class FileRep extends FileSystemObject { constructor(path: string, public content: string) { super(path, false); } } class Directory extends FileSystemObject { children: FileSystemObject[]; } interface Networked { host: string; } const fso: FileSystemObject = new FileRep("foo/bar.txt", "foo"); if (fso.isFile()) { fso.content; // 类型守卫确保此处fso是FileRep类型 } else if (fso.isDirectory()) { fso.children; // 类型守卫确保此处fso是Directory类型 } else if (fso.isNetworked()) { fso.host; // 类型守卫确保此处fso是Networked类型 }
抽象类
// 抽象类不可以实例化 abstract class Base { abstract getName(): string; printName() { console.log("Hello, " + this.getName()); // 调用子类实现的getName方法 } } const b = new Base(); // Error: 不能创建抽象类的实例 // 必须被继承并实现抽象方法 class Derived extends Base { getName() { return "world"; // 实现父类的抽象方法 } } const d = new Derived(); d.printName(); // 输出: Hello, world
类型的关系
// TS会通过成员去判断两个类型是否一致 class Point1 { x = 0; y = 0; } class Point2 { x = 0; y = 0; } // 正确: Point1和Point2的结构一致 const p: Point1 = new Point2();
另一个例子:
class Person { name: string; age: number; } class Employee { name: string; age: number; salary: number; } // 正确: Employee具有Person的所有属性,因此它们是兼容的 const p: Person = new Employee();
最后一个例子:
class Empty {} function fn(x: Empty) { // x是一个空的类型,所以不能做任何事情 } // 都是正确的! fn(window); fn({}); fn(fn);