TypeScript 入门学习笔记(面向对象 + 常用设计模式)

目录

[一、TS 核心基础语法](#一、TS 核心基础语法)

二、代理模式(委托模式)

模式简介

必知知识点补充

三、简单工厂模式

模式简介

必知知识点补充

[四、观察者模式(发布 - 订阅模式)](#四、观察者模式(发布 - 订阅模式))

模式简介

必知知识点补充

五、单例模式

模式简介

新手必知知识点补充

[TypeScript 基础语法速查表](#TypeScript 基础语法速查表)


一、TS 核心基础语法

先搞懂这些基础语法,再看后续代码会事半功倍,这是 TS 面向对象编程的核心基石。

表格

语法关键字 核心作用 通俗理解
interface 定义类型契约 / 规范,约束类、对象、函数的结构 相当于一份 "合同",签了合同的类,必须实现合同里约定的所有内容
class 定义类,TS 面向对象的核心,用来封装属性和方法 相当于一个 "模板",可以用new关键字根据模板创建实例对象
implements 类实现接口,强制类必须符合接口的结构规范 类 "签订" 了 interface 合同,必须兑现合同里的所有承诺
extends 类的继承,子类可复用父类的属性和方法 子类继承父类的能力,同时可以扩展自己独有的功能
public/private 访问修饰符,控制属性 / 方法的访问权限 public:公开的,任何地方都能访问(TS 默认值);private:私有的,只有类内部能访问
static 静态修饰符,修饰的属性 / 方法属于类本身,而非实例 无需new创建实例,直接用类名.xxx即可访问
enum 枚举,给一组固定取值起友好名称,限定取值范围 把固定选项(如车型、状态)打包管理,避免硬编码的魔法字符串 / 数字
get/set 访问器,拦截属性的读取 / 赋值操作,可嵌入额外逻辑 给属性加 "门禁",读写属性时会先经过门禁,可做校验、触发事件等操作
: 类型 类型注解,给变量 / 参数 / 返回值指定类型,是 TS 类型校验的核心 给数据贴 "类型标签",TS 会检查使用是否合规,提前发现错误
any 任意类型,关闭 TS 的类型校验 相当于给数据 "开绿灯",TS 不再做类型检查,新手慎用,会丢失 TS 的类型优势

二、代理模式(委托模式)

模式简介

代理模式(也叫委托模式),核心思想是:调用者不直接执行具体操作,而是把操作委托给代理对象执行。可以动态切换不同的代理实现,将 "调用操作" 和 "具体实现" 解耦,大幅提升代码扩展性。

复制代码
// 1. 定义接口:约定计算方法的规范(合同)
// 接口名前加I是TS通用规范,代表Interface,方便识别
interface Icalc {
  // 约定:必须有calc方法,接收2个参数,返回值必须是数字类型
  calc(num1: any, num2: any): number;
}

// 2. 实现类1:加法计算,严格遵守Icalc接口的约定
// implements关键字:声明这个类要实现Icalc接口,必须实现接口里的calc方法
class Npc1 implements Icalc {
  // 实现接口约定的calc方法,参数、返回值与接口保持一致
  calc(num1: any, num2: any) {
    // 实现加法逻辑,返回两数之和
    return num1 + num2;
  }
}

// 3. 实现类2:减法计算,同样遵守Icalc接口的约定
class Npc2 implements Icalc {
  // 实现接口约定的calc方法,这里编写减法逻辑
  calc(num1: any, num2: any) {
    return num1 - num2;
  }
}

// 4. 调用者类:Person,不自己实现计算逻辑,全部委托给代理对象
class Person {
  // 定义代理属性,类型为Icalc接口
  // 含义:这个属性只能接收"实现了Icalc接口的类的实例"
  // !是TS非空断言,告诉TS该属性一定会在使用前赋值,避免初始化报错
  delegate!: Icalc;

  // 对外暴露的获取计算结果的方法
  Getnum(num1: any, num2: any) {
    // 自身不做计算,委托给代理对象的calc方法执行
    let num = this.delegate.calc(num1, num2);
    // 将结果输出到页面
    document.write(num + "");
  }
}

// 5. 实际使用
// 创建Person实例
let person = new Person();
// 给person设置代理:使用加法实现类Npc1的实例
person.delegate = new Npc1();
// 调用方法,实际执行Npc1的加法逻辑,输出91
person.Getnum(13, 78);

// 【扩展性演示】想换成减法?一行代码切换代理,无需修改Person类的任何代码
// person.delegate = new Npc2();
// person.Getnum(13, 78); // 输出-65

必知知识点补充

  1. 接口的核心价值 接口只定义 "规范",不编写具体实现。所有实现了该接口的类,都有统一的方法名和参数结构,调用时无需关心具体是哪个类,只需知道它具备对应的方法,这就是面向接口编程
  2. 代理模式的优势 比如示例中想要新增乘法、除法逻辑,只需新建一个实现Icalc接口的类,无需修改Person类的代码,符合开闭原则(对扩展开放,对修改关闭)。
  3. 新手易错点提醒
  • 原代码中delegate: Icalc;没有初始化,TS 会报 "属性未初始化" 的错误,解决方案:
    1. 加非空断言!,告诉 TS 该属性一定会在使用前赋值;
    2. 在构造函数中给默认值;
  • 尽量少用any类型,示例中可改为number类型,更符合 TS 的类型规范,比如calc(num1: number, num2: number): number

三、简单工厂模式

模式简介

简单工厂模式(也叫静态工厂模式),核心思想是:封装对象的创建逻辑,对外提供统一的工厂方法。调用者无需关心对象的创建细节,只需传入对应的类型,工厂就会返回匹配的实例对象,将 "对象创建" 和 "对象使用" 解耦。

复制代码
// 1. 定义枚举:限定车型的可选值,避免硬编码魔法字符串
// 枚举会给每个选项默认分配数字,Bmw=0, Audi=1, Benz=2,也可手动赋值
enum CarType {
  Bmw,
  Audi,
  Benz
}

// 2. 父类:汽车基类,所有车型都继承该类
class Car {
  // 公共属性:车名
  name: string;

  // 3. 静态工厂方法:核心,用来创建汽车实例
  // static修饰:该方法属于Car类本身,直接用Car.Create()调用,无需new Car()
  // 参数:车型枚举,返回值:Car类的实例
  static Create(carType: CarType): Car {
    // 定义变量,存储创建好的汽车实例
    let car: Car;
    // 根据传入的车型,创建对应的实例
    switch (carType) {
      case CarType.Audi:
        car = new Audi();
        break;
      case CarType.Benz:
        car = new Benz();
        break;
      case CarType.Bmw:
        car = new Bmw();
        break;
      default:
        throw new Error("不支持的车型");
    }
    // 返回创建好的实例
    return car;
  }
}

// 4. 子类:宝马,继承Car父类
// extends关键字:继承父类的所有属性和方法
class Bmw extends Car {
  constructor() {
    super(); // 必须调用super(),执行父类的构造函数
    this.name = "宝马";
  }
}

// 子类:奔驰,继承Car父类
class Benz extends Car {
  constructor() {
    super();
    this.name = "奔驰";
  }
}

// 子类:奥迪,继承Car父类
class Audi extends Car {
  constructor() {
    super();
    this.name = "奥迪";
  }
}

// 5. 实际使用
// 调用工厂方法,传入车型,直接拿到对应实例,无需自己new Benz()
let bmw = Car.Create(CarType.Benz);
console.log(bmw.name); // 输出:奔驰

// 想创建奥迪,只需修改传入的枚举值,无需关心Audi类的创建细节
let audi = Car.Create(CarType.Audi);
console.log(audi.name); // 输出:奥迪

必知知识点补充

  1. 枚举的优势CarType.Bmw代替硬编码的字符串 / 数字,代码可读性更高,且 TS 会做类型校验,只能传入枚举中定义的值,避免传参错误。
  2. static 静态成员的特点 静态属性 / 方法属于类本身 ,而非实例。因此无需new Car()创建实例,直接用Car.Create()即可调用;且静态方法中不能用this访问实例的属性 / 方法,只能访问静态成员。
  3. 继承中的 super () 子类的构造函数中,必须先调用super(),它会执行父类的构造函数,只有调用super()之后,才能在子类构造函数中使用this
  4. 简单工厂的适用场景当需要创建的对象类型不多、创建逻辑不复杂时使用,比如 UI 组件库的弹窗、按钮创建,统一封装创建逻辑。

四、观察者模式(发布 - 订阅模式)

模式简介

观察者模式(也叫发布 - 订阅模式),核心思想是:定义一对多的依赖关系,当一个对象(被观察者 / 主题)的状态发生变化时,会自动通知所有依赖它的观察者对象,观察者收到通知后执行对应逻辑。这是前端最常用的模式之一,Vue 的响应式、事件监听、状态管理都基于这个核心思想。

复制代码
// 1. 定义观察者接口:约定观察者必须实现的方法
interface IObserver {
  // 约定:被观察者状态变化时,会调用该方法,传入新的状态值
  nameChanged(newName: string): void;
}

// 2. 被观察者类:Person,也叫主题/发布者
class Person {
  // 私有属性:真正存储名字的变量,加_是TS通用规范,代表私有属性
  // private修饰符:只有Person类内部能访问,外部无法直接修改
  private _name: string;

  // 观察者数组:存放所有注册的观察者实例
  // Array<IObserver>:泛型数组,只能存放实现了IObserver接口的实例
  observers: Array<IObserver> = new Array<IObserver>();

  // set访问器:拦截对name属性的赋值操作
  // 当给person.name赋值时,会自动执行这个set方法
  set name(value: string) {
    // 给私有属性赋值,更新状态
    this._name = value;
    // 【核心】状态变化后,遍历所有观察者,给每个观察者发送通知
    for (let observer of this.observers) {
      // 调用观察者的nameChanged方法,传递新值
      observer.nameChanged(this._name);
    }
  }

  // get访问器:拦截对name属性的读取操作
  // 当读取person.name时,会自动执行这个get方法
  get name() {
    // 返回私有属性的值
    return this._name;
  }
}

// 3. 观察者类:Test,实现了IObserver接口
class Test implements IObserver {
  // 实现接口约定的方法,收到通知后执行的业务逻辑
  nameChanged(newName: string) {
    // 这里可编写任意业务逻辑,比如更新UI、触发接口请求等
    document.write("监听到变化,名字变为" + newName);
  }
}

// 4. 实际使用
// 创建被观察者实例
let person = new Person();
// 创建观察者实例
let test = new Test();
// 将观察者注册到被观察者的数组中,相当于"订阅"状态变化
person.observers.push(test);

// 给name赋值,触发set访问器,自动通知所有观察者
// 执行这行代码后,页面会输出"监听到变化,名字变为哈哈哈"
person.name = "哈哈哈";

// 【扩展性演示】可添加无数个观察者,都会收到通知
// class Test2 implements IObserver {
//   nameChanged(newName: string) {
//     console.log("Test2也监听到了:", newName);
//   }
// }
// person.observers.push(new Test2());
// person.name = "新名字"; // 两个观察者都会收到通知

必知知识点补充

  1. get/set 访问器的作用 它可以拦截属性的读写操作,我们可以在其中嵌入额外逻辑,比如赋值校验、触发事件、日志打印等,Vue2 的响应式原理就是基于Object.defineProperty的 get/set 实现的。
  2. 观察者模式的核心优势被观察者和观察者完全解耦:被观察者只负责 "发布通知",无需关心观察者收到通知后的行为;观察者只负责 "处理通知",无需关心状态何时变化。可以随时新增 / 删除观察者,无需修改原有代码。
  3. for...of 遍历用来遍历数组的每一项,比传统 for 循环更简洁,新手可直接用它遍历数组。
  4. 泛型数组Array<T> Array<IObserver>的含义是 "这个数组里只能存放实现了 IObserver 接口的实例",TS 会做类型校验,放入其他类型会直接报错,这就是泛型的类型约束作用。

五、单例模式

模式简介

单例模式,核心思想是:保证一个类在全局范围内只有一个实例,并且提供一个全局统一的访问点。比如全局状态管理、音频管理器、弹窗管理器,都适合用单例实现,既能避免重复创建实例浪费资源,也能保证全局状态的一致性。

复制代码
// 全局音频管理器,单例模式实现
class SoundManager {
  // 1. 私有静态属性:存放全局唯一的实例
  // private:外部无法直接访问
  // static:属于类本身,而非实例
  private static instance: SoundManager;

  // 2. 私有构造函数!单例实现的核心
  // 构造函数加了private,外部就无法用new SoundManager()创建实例
  // 从根源上限制了实例的创建,保证全局只有一个实例
  private constructor() {}

  // 3. 静态方法:全局唯一的访问点,用来获取实例
  static Instance() {
    // 【懒加载核心】判断实例是否已经创建
    if (!SoundManager.instance) {
      // 只有第一次调用时,才会创建实例
      SoundManager.instance = new SoundManager();
    }
    // 永远返回同一个实例
    return SoundManager.instance;
  }

  // 给类添加业务方法,比如播放音频
  playSound() {
    console.log("播放音频");
  }
}

// 4. 实际使用
// 永远只能通过SoundManager.Instance()获取实例,禁止new
const manager1 = SoundManager.Instance();
const manager2 = SoundManager.Instance();

// 验证:两个变量指向同一个实例,输出true
console.log(manager1 === manager2);

// 调用业务方法
manager1.playSound(); // 输出:播放音频

// 【错误演示】构造函数是private,外部new会直接报错
// const manager3 = new SoundManager(); // 报错:构造函数是私有的,仅可在类声明中访问

新手必知知识点补充

  1. 私有构造函数的作用 普通类的构造函数是 public 的,外部可以随时new创建实例;构造函数加了private之后,只有类内部能调用new,外部完全无法创建实例,从根源上限制了实例的数量。

  2. 懒加载(懒汉式)的优势 上面的写法是懒汉式单例,只有第一次调用Instance()方法时,才会创建实例。如果这个类从未被使用,就不会创建实例,节省内存资源。

  3. **饿汉式单例(另一种常用写法)**还有一种饿汉式写法,类加载时就直接创建实例,代码更简洁:

    class SoundManager {
    // 类加载时就直接创建实例,赋值给静态属性
    private static instance: SoundManager = new SoundManager();
    private constructor() {}
    // 直接返回已经创建好的实例
    static Instance() {
    return SoundManager.instance;
    }
    }

两者区别:饿汉式是提前创建,懒汉式是使用时再创建,新手可根据场景选择。4. 单例的适用场景全局只需要一个的对象,比如全局配置、日志管理器、资源加载器、全局弹窗等,保证全局状态统一,避免资源浪费。

TypeScript 基础语法速查表

这张表将 TS 新手必备的所有核心语法整合在一起,涵盖变量声明、数据类型、函数、面向对象、模块等全方面内容,方便随时查阅。

分类 语法 / 关键字 核心作用 新手通俗理解 代码示例
变量声明 let 声明块级作用域变量,可重新赋值 最常用的变量声明方式,替代旧的 var,只在当前 {} 内有效 let age = 25;``age = 26; ✅ 可重新赋值
const 声明块级作用域常量,声明后不可重新赋值 用来声明不会变化的固定值,声明时必须赋值,保证数据不可变 const PI = 3.14;``const user = { name: "张三" };``user.name = "李四"; ✅ 对象内容可改
var 声明函数级作用域变量,有变量提升 ❌ 新手别用!历史遗留语法,作用域混乱 var a = 10; // 不推荐
基础数据类型 string 字符串类型 文本内容,用单引号 / 双引号 / 反引号包裹 let name: string = "张三";``let greet: string = \Hello, ${name}`;`
number 数字类型 整数、小数、正数、负数都可以 let age: number = 25;``let price: number = 99.99;
boolean 布尔类型 只有两个值:true(真)和 false(假) let isLogin: boolean = true;
null 空值类型 表示 "什么都没有",是一个空对象引用 let data: null = null;
undefined 未定义类型 变量声明了但没赋值,默认就是 undefined let temp: undefined = undefined;
void 空返回值类型 专门用于函数,表示函数没有返回值 function log(msg: string): void { console.log(msg); }
never 永不存在值类型 表示函数永远不会正常结束(抛错 / 死循环) function throwError(msg: string): never { throw new Error(msg); }
unknown 未知类型 安全版的 any,使用前必须先做类型检查 let value: unknown = "hello";``if (typeof value === "string") { value.toUpperCase(); }
any 任意类型 关闭 TS 类型校验,相当于 "开绿灯",新手慎用! let anything: any = 10;``anything = "hello"; // 不报错但不安全
复杂数据类型 类型[] / Array<类型> 数组类型 存放一组相同类型的数据 let nums: number[] = [1, 2, 3];``let names: Array<string> = ["张三", "李四"];
{ 属性名: 类型; ... } 对象类型 描述对象的结构,约束对象的属性和类型 let user: { name: string; age: number } = { name: "张三", age: 25 };
type 别名 = 类型 类型别名 给复杂类型起个简短的名字,方便复用 type User = { name: string; age: number };``let user: User = { name: "张三", age: 25 };
`类型1 类型2` 联合类型 表示值可以是多种类型中的一种 **`let value: string number = "hello";``value = 100;` ✅ 两种都可以**
类型1 & 类型2 交叉类型 表示值必须同时满足多种类型 type A = { name: string };``type B = { age: number };``let user: A & B = { name: "张三", age: 25 };
(参数: 类型) => 返回值 函数类型 描述函数的参数类型和返回值类型 type AddFn = (a: number, b: number) => number;``const add: AddFn = (a, b) => a + b;
值 as 类型 类型断言 告诉 TS"我比你更清楚这个值的类型",强制指定 let value: unknown = "hello";``let len: number = (value as string).length;
函数语法 function 函数名(参数: 类型): 返回值 { } 函数声明 定义一个有名字的函数 function add(a: number, b: number): number { return a + b; }
参数?: 类型 可选参数 表示这个参数可以传也可以不传,不传为 undefined function greet(name?: string): string { return \Hello, ${name
参数: 类型 = 默认值 默认参数 给参数设置默认值,不传参时自动使用 function greet(name: string = "陌生人"): string { return \Hello, ${name}`; }`
...参数名: 类型[] 剩余参数 把多个剩余参数收集成一个数组 function sum(...nums: number[]): number { return nums.reduce((a, b) => a + b, 0); }
面向对象 interface 接口名 { } 定义接口 定义类型契约 / 规范,约束类、对象的结构 interface User { name: string; age: number; }
class 类名 { } 定义类 面向对象的核心,封装属性和方法的模板 class Person { name: string; sayHi() { console.log("Hi"); } }
class 类名 implements 接口名 { } 类实现接口 强制类必须符合接口的结构规范 interface IUser { name: string; }``class User implements IUser { name: string; }
class 子类 extends 父类 { } 类的继承 子类继承父类的所有属性和方法,同时可扩展 class Animal { name: string; }``class Dog extends Animal { bark() { console.log("汪汪"); } }
public 公共访问修饰符 公开的,任何地方都能访问(TS 默认值,可省略) class Person { public name: string; }
private 私有访问修饰符 私有的,只有类内部能访问,外部和子类都不行 class Person { private _age: number; }
protected 受保护访问修饰符 受保护的,只有类内部和子类能访问,外部不行 class Person { protected gender: string; }
readonly 只读修饰符 修饰的属性只能读取,不能修改 class Person { readonly id: number; constructor(id: number) { this.id = id; } }
static 静态修饰符 修饰的属性 / 方法属于类本身,直接用类名调用 class Utils { static PI = 3.14; }``Utils.PI; // 直接调用
get / set 访问器 拦截属性的读取 / 赋值操作,可嵌入额外逻辑 class Person { private _name: string; get name() { return this._name; } set name(value) { this._name = value; } }
abstract class 抽象类 不能直接 new 的类,专门用来被继承 abstract class Animal { abstract makeSound(): void; }
abstract 方法名() 抽象方法 只有方法签名,没有具体实现,子类必须实现 abstract class Animal { abstract makeSound(): void; }``class Dog extends Animal { makeSound() { console.log("汪汪"); } }
其他常用 enum 枚举名 { 选项1, 选项2 } 枚举 给一组固定取值起友好名称,限定取值范围 enum Color { Red, Green, Blue }``let c: Color = Color.Red;
typeof 变量 类型判断 获取变量的类型(运行时有效),常用于类型守卫 let value: unknown = "hello";``if (typeof value === "string") { value.toUpperCase(); }
变量 instanceof 类 实例判断 判断变量是否是某个类的实例(运行时有效) class Dog { }``let d = new Dog();``if (d instanceof Dog) { d.bark(); }
keyof 类型 键名获取 获取类型的所有键名,返回一个联合类型 type User = { name: string; age: number };``type UserKeys = keyof User; // "name" | "age"
<T> 泛型 让类型也可以像参数一样传递,实现类型复用 function identity<T>(value: T): T { return value; }``identity<string>("hello");
import / export 模块导入导出 用于模块化开发,在不同文件间共享代码 // a.ts``export const PI = 3.14;``// b.ts``import { PI } from "./a.ts";
相关推荐
晚霞的不甘2 小时前
HarmonyOS ArkTS 进阶实战:深入理解边距、边框与嵌套布局
前端·计算机视觉·华为·智能手机·harmonyos
_野猪佩奇_牛马版2 小时前
ReACT Agent 开发知识点总结
前端
牛奶2 小时前
你发送的消息,微信到底怎么送到的?
前端·websocket·http
酉鬼女又兒2 小时前
零基础快速入门前端DOM 元素获取方法详解:从代码到实践(可用于备赛蓝桥杯Web应用开发)
前端·javascript·职场和发展·蓝桥杯·js
牛奶2 小时前
为什么关掉浏览器再打开,你还是登录状态?
前端·网络协议·https
bjxiaxueliang2 小时前
一文掌握Python aiohttp:异步Web开发从入门到部署
开发语言·前端·python
Liudef062 小时前
从0到1开发ReAct智能体:原理、实现与最佳实践
前端·react.js·前端框架
北岛寒沫2 小时前
北京大学国家发展研究院 中国经济专题 课程笔记(第三课 人口与劳动力)
经验分享·笔记·学习
observe1012 小时前
arm汇编语言学习
学习