> For the complete documentation index, see [llms.txt](https://yangsx95.gitbook.io/notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yangsx95.gitbook.io/notes/programming-language/typescript/mian-xiang-dui-xiang.md).

# 面向对象

## 类：class

```typescript
class Person {

    // 静态属性
    static readonly lang: string = "chinese";

    // 静态只读属性，类似const
    static readonly happy: boolean = true;

    // 成员属性/实例属性
    name: string = "孙悟空";

    // 成员属性没有默认值
    age: number;

    // 只读成员属性
    readonly sex: string;

    // 所有属性必须初始化，无论是不是只读属性，而java中只有只读属性(final)才必须初始化
    // 这是构造器，构造器不可以重载
    constructor(age: number, sex: string) {
        this.age = age; // this指代当前对象
        this.sex = sex;
    }

    // 静态方法
    static sayHello() {
        console.log("hello");
    }

    // 成员方法
    printHello() {
        console.log("hello");
    }
}

// 根据类创建对象
const p = new Person(10, "男");
// 访问静态属性
console.log(Person.lang);
// 访问静态方法
Person.sayHello()
// 访问成员属性
console.log(p.age)
// 访问成员方法
p.printHello()
```

## 封装

```typescript
class Person {
    // 下划线是内置命名，但是无法放置其他程序访问
    // 他会与get/set配合
    // private代表私有属性
    private _name: string;
    private _age: number;
    // public代表公开属性
    public happy: boolean = true;
  
  	// 还有一个protected属性，只可以被子类使用
  	// private 不能被子类使用

    constructor(name: string, age: number) {
        this._name = name;
        this._age = age;
    }

    // 提供 getter/setter 获取内部属性，这些方法默认为public的
    get name(): string {
        return this._name;
    }

    set name(value: string) {
        this._name = value;
    }

    get age(): number {
        return this._age;
    }

    set age(value: number) {
        if (value < 0 || value > 120) {
            throw new Error("年龄不能小于0大于120");
        }
        this._age = value;
    }
}

// 通过get/set访问属性
let p: Person = new Person("张三", 18);

// 可以直接通过.的方式访问变量的get/set方法
console.log(p.name); // 访问 _age 的get方法
p.age = -1; // 访问 _name 的set方法
```

执行结果：

```
                throw new Error("年龄不能小于0大于120");
                ^

Error: 年龄不能小于0大于120
    at Person.set (/Users/yangsx/Project/temp/nots-ts/src/a.js:25:23)
    at Object.<anonymous> (/Users/yangsx/Project/temp/nots-ts/src/a.js:38:7)
    at Module._compile (node:internal/modules/cjs/loader:1095:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1124:10)
    at Module.load (node:internal/modules/cjs/loader:975:32)
    at Function.Module._load (node:internal/modules/cjs/loader:816:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:79:12)
    at node:internal/main/run_main_module:17:47
```

> 解决不可以使用get 和 set的问题：tsc -t es5 a.ts

### public、protected、private

* `public`，都可以访问
* `protected`是提供给子类访问的，外部不可访问
* `private`是私有的，只有自己内部可以访问

### 属性快速写法

下面两种写法是等同的：

```typescript
class Person {
    private _name:string;
    private _age: number;
    
    constructor(name: string, age: number) {
        this._name = name;
        this._age = age;
    }
}
```

```typescript
class Person {

    constructor(private name: string, private age: number) {
    }
    
}
```

## 继承

```typescript
class Animal {
    protected _name: string;

    constructor(name: string) {
        this._name = name;
    }

    public sayHello(): void {
        console.log("动物叫~")
    }
}

class Dog extends Animal {
}

class Cat extends Animal 
}

// 子类继承了父类的所有非私有方法和属性，包括构造器
let d: Dog = new Dog("旺财");
let c: Cat = new Cat("猫大姐");
```

### 多继承：不支持

不支持多继承。

### 子类新增方法

给狗增加跑的方法：

```typescript
class Dog extends Animal {
    public run():void {
        console.log(`${this._name}在跑~`)
    }
}
d.run(); // dog类对象才能调用run
```

### 方法重写

```typescript
class Dog extends Animal {
    public run():void {
        console.log(`${this._name}在跑~`)
    }
    
    override sayHello() {
        console.log("汪汪");
    }
}

class Cat extends Animal {
    override sayHello() {
        console.log("喵喵");
    }
}
```

### super关键字

```typescript
class Cat extends Animal {
    override sayHello() {
        super.sayHello(); // 先调用父类的sayHello方法
        console.log("喵喵");
    }
}
```

### 构造器重写

```typescript
class Cat extends Animal {
    constructor() {
        super("猫大姐"); // 必须调用父类的构造
    }

    override sayHello() {
        super.sayHello(); // 必须先调用父类的sayHello方法，不写不可
        console.log("喵喵");
    }
}
let c: Cat = new Cat();
```

## 抽象类

抽象类不可以用于创建实例。抽象类中可以定义抽象方法，抽象方法没有方法体，且必须必须被重写：

```typescript
abstract class Animal { 
    protected _name: string;

    constructor(name: string) {
        this._name = name;
    }

    public abstract sayHello(): void;
}

class Dog extends Animal {
    public run(): void {
        console.log(`${this._name}在跑~`)
    }

    sayHello() { // 因为是抽象方法，override可以省略
        console.log("汪汪");
    }
}

class Cat extends Animal {
    constructor() {
        super("猫大姐"); // 必须调用父类的构造
    }

    override sayHello() { 
        super.sayHello(); // 先调用父类的sayHello方法
        console.log("喵喵");
    }
}
```

## 接口

```typescript
// 接口不仅可以定义方法，还可以定义属性
interface A {
    name: string;
    age: number;

    sayHello(): void;
}

interface B {}

// 实现接口
// 接口可以多实现
class B implements A, B {
    age: number;
    name: string;

    constructor(age: number, name: string) {
        this.age = age;
        this.name = name;
    }

    sayHello(): void {
        console.log(`hello ${this.name}`)
    }

}
```

### 函数式接口

```javascript
interface Action {
    (event: string): void
}
transition: function (event: string, action: Action) {
	action()
}
```
