前言
该篇文章主要介绍一些在前端中的设计模式及其应用,如单例模式、发布订阅模式、原型模式、代理模式等。
1. 概述
❝
什么是设计模式
❞
设计模式是软件设计中常见问题的解决模型:
-
是历史经验的总结 -
与特定语言无关,是实现某种需求的方法
❝
设计模式趋势
❞

❝
设计模式分类
❞
有23种设计模式分为:
-
创建型 – 如何创建一个对象 -
结构型 – 如何灵活的将对象组装成较大的结构 -
行为型 – 负责对象间的高效通信和职责划分
2. 浏览器API中的设计模式
2.1 单例模式
「定义」
-
全局唯一访问对象,在任何地方访问都会返回唯一对象,在任何地方修改都会修改该唯一对象
「应用场景」
-
web应用中的全局缓存 -
如react、vue中的状态管理
「举例」
//用单例模式实现请求缓存
import {api} from '../utils/index'
export class Request{
static instance: Request;
private cache: Record<string,string>;
constructor(){
this.cache = {};
}
static getInstance(){
if(this.instance){
return this.instance;
}
this.instance = new Request();
return this.instance;
}
public async request(url:string){
if(this.cache[url]){
return this.cache[url];
}
const response = await api(url);
this.cache[url] = response;
return response;
}
}
Request
类的instance
属性存储全局唯一的Request
对象,cache
存储全局缓存。
Request
类通过getInstance
静态方法来获取全局唯一的 Request
对象,通过request
公共方法来对请求进行包装,当这个url
已经请求过就取cache
中对应url
的缓存数据。
-
用单例模式实现请求缓存:用方法实现
//用单例模式实现请求缓存
import {api} from '../utils/index'
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;
}
2.2 发布订阅模式(观察模式)
「定义」
-
一种订阅机制,可在被订阅对象发生变化时通知订阅者
「应用场景」
-
从系统架构之间的解耦(消息队列,redis缓存),到业务中一些实现模式,像邮件订阅,上线订阅等,应用广泛。 -
如vue中组件之间通信方式
「举例」
-
浏览器中的事件绑定 -
我们给按钮button绑定一个事件 click
,点击按钮后就触发函数doSomthing...
-
订阅对象就是按钮button,使订阅对象发生 变化的契机就是点击按钮,订阅者就是绑定的函数 doSomting..
,点击按钮后订阅者就做一些事情。

-
用发布订阅模式实现用户上线订阅
User
类:
-
name
属性定义创建对象时,对象的名称。 -
status
属性用于表示该用户是否上线:offline
下线,online
上线 -
followers
:用于存储订阅该对象的数组,数组中存储订阅该对象的对象以及传入的函数。
构造函数用于完成对象的初始化。
-
subscribe
函数用于完成订阅对象这个行为,user
参数传入要订阅的对象,notify
传入函数,然后放入被订阅者的followers
数组中。 -
online
函数用来执行对象上线的动作,并通知订阅者,执行订阅的函数。
测试:
创建三个对象user1
user2
user3
,user1
user2
去订阅user3
,传入执行函数。user3
上线就执行订阅者user1
user2
的传入的函数。
3. JavaScript中的设计模式
3.1 原型模式
「定义」
-
复制已有对象来创建新的对象
「应用场景」
-
JavaScript中对象创建的基本模式
「举例」
用原型模式创建上线订阅中的用户我们通过对象字面量来存储
User
对象的相关属性和方法。通过createUser
方法来创建User
对象,通过该方法中的Object,create
,会基于已有对象来返回一个新的对象
3.2 代理模式
「定义」
-
可自定义控制对原对象的访问方式,并且允许在更新前后做一些额外处理
「应用场景」
-
监控,代理工具,前端框架实现(vue中的数据劫持,)等等。
「举例」
使用代理模式实现用户状态订阅:
-
我们上边的例子中
online
函数做了两件事情,一是用户上线,二是通知订阅者。 -
我们在实际开发中应尽量满足一个函数只做一件事,那么我们可以通过代理模式来实现通知订阅者,而
online
函数只进行对象上线操作,如下图。
实现真正的通知订阅者行为:
-
通过
createProxyUser
来创建User
的代理对象,通过创建Proxy
,传入被代理对象user
,以及传入需要进行的操作,有两个函数get
,set
,get
用于获取user
对象时进行一些操作,set
用户设置user
对象时进行一些操作,这里我们只需要set
。 -
set
函数参数target
指代被代理对象user
,prop
指定被代理对象的属性,value
表示设置的值。 -
这个
set
函数做的事情是,当对被代理对象中的属性赋值时,先进行赋值操作(this.status = "online"
),再判断该prop
属性是不是status
,如果是就执行notifyStatusHandlers
函数;该函数会判断传入值value
是不是online
,如果是就通知订阅者该用户已上线。
3.3 迭代器模式
「定义」
-
在不暴露数据类型的情况下访问集合中的数据
「应用场景」
-
数据结构中有多种数据类型,列表、数等,提供通用操作接口。
「举例」
创建一个MyDomElement
类,tag
属性表示组件名称,children
表示承载该组件的所有子组件数组,addChildren
用来添加子组件。
通过Symbol.iterator
方法将对象变成可迭代对象;这个方法返回一个对象,对象中有一个函数next
,会遍历出所有的子组件,进行返回;返回值是一个对象,有两个参数:vaule
表示返回的值,done
表示是否迭代完成。
4. 前端框架中的设计模式
主要介绍代理模式和组合模式
4.1 代理模式
「举例」
Vue 组件实现计数器
这是实现的代码,通过点击按钮,使
count
+1,但是为什么页面会自动更新,不应该进行DOM操作吗,这就是我们要讲的前端框架中对DOM操作的代理。

先看有框架前后的视图更新我们是怎么做的:
-
前:当我们更新DOM属性
count
时,我们通过innerText
来更新视图中的count
值。 -
后:当我们更新DOM属性
count
时,先有框架对虚拟DOM更新,然后通过Diff
算法比对何处变化,由框架来完成视图的更新,我们只需要更新数据,就会自动更新视图。
DOM更新前后的钩子:当我们点击按钮count
值+1时,就会输出更新前后的值
4.2. 组合模式
「定义」
-
可多个对象组合使用,也可单个对象独立使用
「应用场景」
-
DOM结构,前端组件(多个组件组成一个页面),文件目录,部门
「举例」
React的组件结构:
调用setCount
时,设计一个队列对count进行主动更新,也是对DOM做代理,但不是JavaScript中的Proxy
。
Count
组件可以作为一个独立的组件进行渲染,也可以作为一个组件的一部分。
5. 总结
-
总结出抽象的模式相对比较简单,但是想要将抽象的模式套用到场景中非常困难。 -
现代编程语言的多编程范式带来的更多可能性,响应式编程,函数式编程。 -
可以通过优秀的开源项目学习设计模式并不断实践。
原文始发于微信公众号(yanghi):前端设计模式
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/226874.html