前端常用的几种设计模式
概念
设计模式是一套被反复使用的代码设计经验的总结,代表了最佳实践。
设计原则
- S - Single Responsibility Principle 单一职责设计模式
- 一个程序只做好一件事
- 如果功能过于复杂就拆开,每个部分保持独立
- O - OpenClosed Principle 开放/封闭模式
- 对扩展开放,对修改封闭
- 增加需求时,扩展新代码,而非修改已有代码
- L - LisKov Substitution Principle 里式替换原则
- 子类能覆盖父类
- 父类能出现的地方子类就能出现
- I - Interface segregation Principle 接口隔离原则
- 保持接口的单一独立
- 类似单一原则,这里更关注接口
- D - Dependency Inversion Principle 依赖倒转原则
- 面向接口编程,依赖于抽象而不依赖于具象
- 使用方只关注接口而不关注具体实现
发布-订阅模式(观察者模式)
定义一对一或一对多的依赖关系,当发布者发生变化时,订阅方都会收到通知。
异步编程中可以利用这种模式传递回调函数。比如请求成功或错误等事件。无需关系异步操作运行的内部状态,只需要订阅异步执行完成这个节点。
例子:DOM 节点绑定事件函数,这时 DOM 节点就是发布者,事件函数就是订阅者,
1 | const btn=document.getElementById('btn'); |
1 | class Publish { |
单例模式
确保一个类只有唯一一个实例,并且提供一个访问它的全局访问点。
举例:Window,全局缓存,全局状态管理
单例模式只实例化一次,多次调用实例化函数返回的都是第一次创建的实例对象。
1 | //单例模式 |
实际例子
1 | (function () { |
工厂模式
为了不暴露创建对象的具体逻辑,将逻辑封装在一个函数中,本质上市一个负责生产对象实例的工厂。
根据抽象程度分类:简单工厂,工厂方法,抽象工厂
例子:生产角色
- 简单工厂模式 (适合创建的类比较少的)
1 | let UserFactory = function (role) { |
- 工厂方法 (创建多类对象)
1 | let UserFactory = function (role) { |
- 抽象工厂(创建父类,子类继承父类,具体实现在子类)
1 |
|
适配器模式
解决两个接口不兼容的情况。
1 | class Plug { |
例如在项目中拿到的时间是时间戳,我们要展示成日期,就需要一个转换函数。
代理模式
代理是为了控制对象的访问,不让外部对象直接访问到对象。
图片懒加载
缓存代理 (缓存请求结果、计算结果)
1 | const multi = function () { |
- 虚拟代理
某一个花销很大的操作,可以通过虚拟代理的方式延迟到这种需要它的时候才去创建
例:使用虚拟代理实现图片懒加载- 创建了一个 Image 对象,并为其绑定了 onload 事件。
- 将 imgNode 先设置为 ‘./loading.gif’ 加载的菊花图。
- 当 Image 对象加载完真实的图片,也就是上文的 ‘./reality.png’ ,将 imgNode 设置为 ‘./reality.png’。
1 | const imgFunc = ( |
外观模式
提供一个接口,隐藏内部的逻辑,更加方便外部调用
用于封装 JS 类库,通过封装一些接口用于兼容多浏览器
- 写一个通用的事件侦听器函数
1 | const EventUtils = { |
兼容浏览器阻止冒泡、默认事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21let N = window.N || {};
N.tools = {
stopPropagation(e) {
if (e.stopPropagation) {
e.stopPropagation();
} else {
e.cancelBubble = true;
}
},
preventDefault(e) {
if (e.preventDefault) {
e.preventDefault();
} else {
e.returnValue = false;
}
},
stopEvent(e) {
this.stopPropagation(e);
this.preventDefault(e);
}
};
装饰者模式
不需要改变已有的接口,作用是给对象添加功能。
类装饰器,属性装饰器
ES7 的装饰器语法
1 | function readonly(target, key, descriptor) { |