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);

posted on 2022-03-08 18:52  完美前端  阅读(101)  评论(0)    收藏  举报

导航