什么是设计模式?
软件设计常见问题的解决方案模型
- 历史经验的总结
- 与特定语言无关
设计模式的趋势
23种设计模式的分类:
- 创建型-如何创建一个对象
- 结构型-如何灵活的将对象组装成较大的结构
- 行为型-负责对象间的高校通信和职责划分
浏览器中的设计模式
- 单例模式
- 发布订阅模式
单例模式
定义: 全局唯一访问对象
单例模式是一种对象创建模式,它用于产生一个对象的具体实例,它可以确保系统中一个类只产生一个实例
确保一个类只有一个实例,并提供一个全局访问点。它通常用于管理共享资源或全局状态。
单例模式的主要目的是限制一个类的实例化次数,以及确保在整个应用程序中只能有一个相同的对象实例被使用。这在某些情况下是非常有用的,例如需要一个全局共享的对象,或者需要控制资源的访问次数。
实现单例模式的关键是将类的构造函数设置为私有,防止外部通过调用构造函数来创建实例。然后通过一个静态方法来获取实例,并在第一次调用该方法时创建实例。随后的调用都会返回之前创建的实例。
通俗理解就是一个类的对象只能拥有一个实例,没有则创建该对象的实例化对象,有则不会重新创建而是返回该实例化后的对象
应用场景: 缓存,全局状态管理
用单例模式实现请求缓存
import { api } from"./utils" ; //定义api,500m后返回
export cLass Requset {//定义请求实例
static instance: Requset;//定义缓存
private cache: Recordestring, string>;
constructor() {//初始化缓存内容
this.cache = {};
}
static getInstance() {
if ( this. instance) {
return this. instance;
}
this. instance = new Requset();
return this. instance;
}
pubLic async request(url: string) {
if (this . cache[urU]){
return this . cache[urL];
}
const response = await api(urL);
this . cache[urL] = response;
return response;
}
}
test("should response more than 500ms with class", async () = {
const request = Requset. getInstance();
const startTime = Date . now();
await request . request(" /user/1");
const endTime = Date. now();
const costTime = endTime - startTime;
expect ( costTime) . toBeGreaterThanOrEquaL(500);
});
test("should response quickly second time with class", async () = {
const request1 = Requset . getInstance();
await request1 . request(" /user/1");
const startTime = Date now();
const request2 = Requset . getInstance();
await request2 . request(" /user/1");
const endTime = Date . now();
const costTime = endTime - startTime;
expect ( costTime) . toBeLessThan(50);
});
以上是一个传统的单例模式的情景,是一个传统的面向对象的内容
那么如何用JavaScript不用Class来实现单例?
import { api } from "./utils";
const cache: Record<string, string> = {};
export const request = async (urT: string) →{
if (cache[urL]) {
return cache[urL];
}
const response = await api(urL);
cache[urL] = response;
return response;
};
发布订阅模式
定义: 【又称观察者模式】
定义了对象之间的一对多依赖关系,当一个对象状态变化时,它的所有依赖对象都会收到通知并自动更新。广播通信模式(Pub/Sub)是观察者模式的一种实现。
- 一种订阅机制,可在被订阅对象发生变化时通知订阅者
应用场景:
从系统架构之间的解耦,到业务中一些实现模式,像邮件订阅,上线订阅等等,应用广泛
在观察者模式中,存在两种关键角色:
- 主题(Subject):也称为可观察对象(Observable),主题维护一组观察者,并提供方法来添加、移除和通知观察者。主题的状态变化会导致观察者的更新。
- 观察者(Observer):观察者订阅主题,以接收主题的通知。当主题的状态发生变化时,观察者会被通知,并根据需要执行相应的操作。
用发布订阅模式实现用户上线订阅
type Notify = (user: User) => void;
export class User {
name: string;
status: "offline" | "online";
// user 订阅自己的人,notify 上线时的通知函数
followers: { user: User; notify: Notify }[];
constructor(name: string) {
this.name = name;
this.status = "offline";
this.followers = [];
}
// 订阅参数中的 user
subscribe(user: User, notify: Notify) {
user.followers.push({ user, notify });
}
// 状态online——上线
online() {
// 状态改为 online
this.status = "online";
// 通知所有订阅自己的人
this.followers.forEach(({ notify }) => {
notify(this);
});
}
}
test("should notify followers when user is online for multiple users", () => {
// 创建三个用户
const user1 = new User("user1");
const user2 = new User("user2");
const user3 = new User("user3");
// 通知 user1 和 user2 的函数
const mockNotifyUser1 = jest.fn();
const mockNotifyUser2 = jest.fn();
// user1 订阅了 user3 的上线,传入通知 user1 的函数
user1.subscribe(user3, mockNotifyUser1);
user2.subscribe(user3, mockNotifyUser2);
// user3 上线
user3.online();
// user3 会调用通知 user1 的函数
expect(mockNotifyUser1).toBeCalledWith(user3);
expect(mockNotifyUser2).toBeCalledWith(user3);
});
JavaScript中的设计模式
- 原型模式
- 代理模式
- 迭代器模式
原型模式
JavaScript中有一个特性叫原型链,用来实现继承等
定义:
复制已有对象来创建新的对象
应用场景:
JS中对象创建的基本模式
代理模式
定义:
可自定义控制对原对象的访问方式,并且允许在更新前后做一些额外处理
应用场景:
监控,代理工具,前端框架实现等等
迭代器模式
定义:
在不暴露数据类型的情况下访问集合中的数据
应用场景:
数据结构中有多种数据类型,列表,树等,提供通用操作接口
用for of 迭代所有组件
// 浏览器中的 DOM 结构
class MyDomElement {
tag: string;
children: MyDomElement[];
constructor(tag: string) {
this.tag = tag;
this.children = [];
}
addChildren(component: MyDomElement) {
this.children.push(component);
}
// 使组件可迭代
[Symbol.iterator]() {
const list = [...this.children];
let node;
return {
// for...of 迭代时调用的函数
next: () => {
while ((node = list.shift())) {
// 层序遍历
node.children.length > 0 && list.push(...node.children);
// value 是迭代出的值,done 是指迭代是否完成
return { value: node, done: false };
}
return { value: null, done: true };
},
};
}
}
前端框架中设计模式
- 代理模式
- 组合模式
vue组件实现计数器
<template>
<button @click="count++">count is: {{ count }}</button>
</template>
<script setup lang="ts">
import { ref } from "vue ";
const count = ref(0);
</script>
前端框架中对·DOM操作的代理
组合模式
定义:
可多个对象组合使用,可也单个对象独立使用
应用场景:
DOM,前端组件,文件目录,部门