这是我参与「第四届青训营 」笔记创作活动的第7天
设计模式
设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案模型。
- 历史试验和错误总结
- 与特定语言无关
- 一定模式 较为抽象 思想
设计模式背景
1977年 模式语言
1994年 设计模式:可复用面向对象软件的基础(更多在java)
设置模式分类
- 创建型-如何创建一个对象
- 结构型-如何灵活的将对象组装成较大的结构
- 行为型-负责对象间的高效通信和职责划分
浏览器中的设计模式
单例模式
定义:全局唯一访问对象
应用场景:缓存,全局状态管理
用单例模式实现请求缓存
Typescript用class实现
import {api} from "./utils";
export class Request{
static instance: Requset;
private cache: Recorde<string, 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[url]) {
return this.cache[url];
}
//调用api并写入缓存
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 = endTine- startTime;
expect(costTime), toBeGreaterThanorEqual(500);
});
//有缓存 两个request对应同一个instance
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= endTine- startTime;
expect(costTime). toBeLessThan (50);
}
不用class实现请求缓存 全局并且唯一的实现请求缓存
js可以更灵活实现单例模式
import {api} from "./utils";
//全局变量
const cache: Record<string, string>= {};
export const request = async (url: string) =>{
if (cache[url]){
return cache [url];
}
const response = await api(url);
没有就写入
cache [url] = response;
return response;
}
test(" should response quickly second time", async () =>{
await request ("/user/1");
const startTime = Date.now ();
await request ("/user/1");
const endTime = Date. now ();
const costTime = endTime - startTime;
expect (costTime).toBeLessThan (50);
});
发布订阅模式(观察者模式)
定义 : 一种订阅机制,可在被订阅对象发生变化时通知订阅者。
应用场景: 从系统架构之间的解耦,到业务中一些实现模式,像邮件订阅,上线订阅等等,应用广泛。
//button就是被订阅者
const button = document.getELementById("button")
const doSomthing1 = () = {
console.log("Send message to user");
}
//订阅者是函数
//面向对象中函数和参数不能传入另一个函数中
const doSomthing2 = () = {
console.log("Log... ");
}
button.addEventListener ("click", doSomthing1);
button. addEventListener ("click", doSomthing2)
//用发布订阅模式实现用户上线订阅
type Notify = (user: User) = void;
export class User{
name: string;
status: "offline"| "online";
followers: { user: User; notify: Notify }[]
//初始化
constructor (name: string){
this.name = name;
this.status = "offline";
this. followers = []
}
}
subscribe (user: User, notify: Notify) {
user. followers.push(f user, notify});
}
online () {
//更改状态
this.status = "online";
//调用订阅状态
this, followers. forEach({notify} ) =>
notify(this);
});
}
}
test("should notify followers when user is online for user prototypes", () →{
const user1 = new User ("user1");
const user2 = new Use("user2");
const user3 =
createUser("user3");
//测试真正函数是否被触发
//通知用户一和二
const mockNotifyUser1 = jest.fn()
const mockNotifyUser2 = jest.fn ()
//user1是订阅者,user3是被订阅
user1. subscribe (user3, mockNotifyUser1)
user2. subscribe (user3, mockNotifyUser2)
user3.online ();
expect (mockNotifyUser1).toBeCalledWith (user3);
expect (mockNotifyUser2). toBeCalledWith(user3);
});
javascript中的设计模式
原型模式
定义:复制已有对象来创建新的对象
应用场景:JS中对象创建的基本模式
使用较少
继承例子
代理模式
定义:可自定义控制对原对象的访问方式(获取对象上进行控制),并且允许在更新前后做一些额外处理
应用场景:监控(前端产品监控浏览器发出的请求成功率,封装一下fetch的返回值),代理工具,前端框架实现等等
使用代理模式实现用户状态订阅
单一职责(Simple Responsibility Pinciple,SRP)是指不要存在多于一个导致类变更的原因。 假设我们有一个类负责两个职责,一旦发生需求变更,修改其中一个职责的逻辑代码,有可能导致另一个职责的功能发生故障。
迭代器模式
定义:在不暴露数据类型的情况下访问集合(关于value数组的集合,set集合,树状/图的结构)中的数据
应用场景:数据结构中有多种数据类型,列表,树等,提供通用操作接口
forof模式
用for of 迭代所有组件
模拟一个dom结构,children子组件
前端框架中的设计模式
代理模式
与js的有些不同
建立click函数,自己累加一,再渲染
count返回一个新对象
ref是代理模式
前端框架中对Dom操作的代理
日常更改动作属性->视图更新
看似更改Dom操作属性->更新代理虚拟的dom->视图更新
组合模式
定义:可多个对象组合使用,可也单个对象独立使用
应用场景:DOM,前端组件,文件目录,部门目录
react的组件结构,对dom的代理操作