单例模式(singleton)
保证一个类仅有一个实例,并提供一个访问它的全局访问点。实现的方法为先判断实例存在与否,如果存在则直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。
使用场景
- 弹窗:无论点击多少次页面上的按钮,都只创建一个弹窗
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>单例模式(singleton)</title>
</head>
<body>
<button id="btn">按钮一</button>
<script>
function createDialog (text) {
let dialog = document.createElement('div');
dialog.innerHTML = text;
document.body.appendChild(dialog);
return dialog;
}
function createSingleton (fn) {
let singleton; // 这行代码只有在第一次被调用时,会执行,因为函数 return 了
return function () {
return singleton || (singleton = fn.apply(this, arguments)) // singleton 就是 createDialog 返回的 dialog
}
}
let createSingletonDialog = createSingleton(createDialog)
document.querySelector('#btn').onclick = function () {
createSingletonDialog('弹窗文本')
}
</script>
</body>
</html>
订阅-发布模式
基于一个
Event Channel(事件调度中心),希望接收通知的对象Subscriber(订阅者)通过自定义事件订阅主题,被激活事件的对象Publisher(发布者)通过发布主题事件的方式通知各个订阅该主题的Subscriber对象。
使用场景
- 事件总线 EventBus
实际项目场景:修改购物车商品数量功能
- 事件调度中心:EventBus
- 订阅者:各个页面,通过 EventBus 的 on 方法,订阅了 'updateCount' 方法,每个页面都自定义了收到购物车数量更新通知后的处理事件
- 发布者:购物车修改数量,通过 EventBus 的 emit 方法,发布 'updateCount'
// bridge.js
var bridge = {};
/**
* 添加事件队列
* @param {String} key 事件名
* @param {Function} func 事件处理函数
*/
function on(key, func) {
if(!bridge[key]){
// 事件名不存在(未添加),初始化事件队列数组,并塞入第一个事件
bridge[key] = [func];
}else{
// 事件名已存在(已添加),push 新的事件进事件队列数组
bridge[key].push(func);
}
}
/**
* 触发事件
* @param {String} key 事件名
* @param {*} params 事件处理函数接收的参数
*/
function emit(key, params) {
// 事件名不存在,不处理
if(!bridge[key]) return;
// 遍历事件名对应的事件队列数组,依次执行所有事件处理函数
for(let v of bridge[key]){
v(params)
};
}
/**
* 移除事件名
* @param {String} key 事件名
*/
function remove(key) {
bridge[key] && delete bridge[key];
}
module.exports = {
on,
emit,
remove
}
页面中使用(比如,在项目中的任意页面操作购物车相关数据):
let bridge = require('../../utils/bridge.js');
// 注册事件
bridge.on('modifyShoppingCart', data => {
})
// 触发事件
bridge.emit('modifyShoppingCart', data => {
})
// 移除事件
bridge.remove('modifyShoppingCart', data => {
})
观察者模式
当对象之间存在一对多的依赖关系时,当这个对象的状态发生改变,所有依赖它的对象都会收到通知,这就是观察者模式。 观察者模式只有两个主体:
- 目标对象(Subject)
- 观察者(Observer)
// 定义目标对象
class Subject {
constructor () {
// 声明观察者列表
this.observerList = []
}
// 添加观察者
addObserver (observer) {
this.observerList.push(observer)
}
// 通知观察者更新
notify (task) {
this.observerList.forEach(item => item.update(task))
}
}
// 定义观察者类
class Observer {
constructor (name, handler) {
this.name = name
this.handler = handler
}
update({ taskName, taskHandler }) {
// 可以根据 taskName 做一些通用处理
this.handler && this.handler()
}
}
let subject = new Subject()
let observer1 = new Observer('obs1', function () {
// 观察者1,收到通知后,执行自己的自定义行为
console.log(`${this.name} 去执行自己的任务啦`)
})
let observer2 = new Observer('obs2', function () {
// 观察者2,收到通知后,执行自己的自定义行为
console.log(`${this.name} 啥都不想干`)
})
subject.addObserver(observer1)
subject.addObserver(observer2)
subject.notify({ taskName: '任务1'})
控制台输出:
观察者模式 与 订阅-发布模式 的对比
表格来源于: juejin.cn/post/705544…
| 设计模式 | 观察者模式 | 发布订阅模式 |
|---|---|---|
| 主体 | Observer观察者、Subject目标对象 | Publisher发布者、Event Channel事件中心、Subscriber订阅者 |
| 主体关系 | Subject中通过observerList记录ObServer | Publisher和Subscribe不想不知道对方,通过中介联系 |
| 优点 | 角色明确,Subject和Observer要遵循约定的成员方法 | 松散耦合,灵活度高,通常应用在异步编程中 |
| 缺点 | 紧耦合 | 当事件类型变多时,会增加维护成本 |
| 使用案例 | 双向数据绑定 | 事件总线EventBus |
策略模式(Strategy)
将定义的一组算法封装起来,使其相互之间可以替换。封装的算法具有一定的独立性,不会随客户端变化而变化。
使用场景
- 表单验证
const FormStrategy = function () {
// 将算法封装在一个对象里
const strategy = {
// 是否为空
isNull: function (value) {
return /\s+/.test(value) ? '请输入内容' : '';
},
// 是否是一个数字
isNumber: function (value) {
return /^[0-9]+(\.[0-9]+)?$/.test(value) ? '' : '请输入数字';
},
// 是否是手机号码
isPhone: function (value) {
return /^1[3456789]\d{9}$/.test(value) ? '' : '请输入正确的手机号';
}
// 是否是邮箱
isEmail: function (value) {
return /^\s+@\s+$/.test(value) ? '' : '请输入正确的邮箱';
}
}
// 只对外暴露调用接口
return {
check: function (type, value) {
// 去除首尾空白符
value = value.replace(/^\s+|\s+$/g, '');
return strategy[type] ? strategy[type](value) : '没有该类型的检测方法';
},
// 添加策略
addStrategy: function (type, fn) {
strategy[type] = fn;
}
}
}
使用:
let username = document.querySelector('.username').value;
let message = FormStrategy.check('isNull', username);
message ? window.alert(message) : '';
参考文章/书籍
-
《JavaScript 设计模式》