目录
前言
单例设计模式
Command 命令模式
Constructor构造器模式
工厂模式Factory
发布订阅设计模式 publish&subscribe
观察者模式
中介者模式
前言
JS设计模式是一种思想,更规范更合理的管理代码(方便维护,升级,扩展,开发)
单例设计模式
单例设计模式Singleton && Command 命令模式
- 最早期的编程化思想(同样的还有:AMD/CMD/commoJS/ES6Module)
- 避免全局变量污染,
- 实现模块之间的相互调用(提供了模块导出方案)
//Object
let obj = {}
//------高级单例------
//公共模块 utils
let utils = (function () {
function throttle(func, wait) {
//....
}
return {
throttle: throttle;
}
})()
// a模块
let AModule = (function () {
utils.throttle();
function fn() { };
function query() { };
return {
query: query
}
})()
//b模块
let BMoudle = (function () {
utils.throttle();
function fn() { };
AModule.query();
return {}
})()
Command 命令模式
在实际的开发过程中,我们还可以基于命令模式管控方法的执行顺序,从而有效的实现出对应的功能
//b模块(实现当前模块下需要完成的所有功能)
let BMoudle = (function () {
utils.throttle();
AModule.query();
//获取数据的
function getData() { };
// 绑定数据的
function binding() { };
//处理事件的
function handle() { };
//处理其他事情的
function fn() { };
return {
//模块的入口(相当于模块的大脑,控制模块的顺序)
init() {
getData();
binding();
handle();
fn();
}
}
})();
BMoudle.init()
Constructor构造器模式
自定义类和实例 ,(站在面向对象的思想上去构建项目)
私有&公有的属性和方法,
编写公共类库&写一些插件组件,
每一次调用插件我们都是创造这个类的实例,即保证了每个实例之间,有自己的私有属性,互不影响也可以保证一些方法属性还是公用的,有效的避免了代码冗余
// ES5
function Fn() {
this.xxx = xxx;
}
Fn.prototype = {
constructor: Fn,
query() { },
//..
}
Fn.xxx = xxx;
//es6
class Fn {
constructor() {
this.xxx = xxx;
}
query() { };
static xxx() { };
}
let f1 = new Fn;
let f2 = new Fn;
工厂模式Factory
简单的工厂模式(一个方法根据传递的参数不同,做了不同的处理)
function factory(options) {
if (options === null) options = {};
if (!/^(object|function)$/i.test(typeof options)) options = {};
let { type, payload } = options;
if (type = 'MYSQL') {
//...
return;
};
if (type = 'SQLSERVER') {
//....
return;
}
}
factory({
type: 'SQLSERVER',
payload: {
root: '',
pass: '',
select: ''
}
})
JQ中的工厂模式(加工转换)
(function () {
function jQuery(selector, content) {
return new jQuery.fn.init(selector, content)
}
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
//.....
};
//中间转换
function init(selector, content, root) { }
jQuery.fn.init = init;
init.prototype = jQuery.fn;
if (typeof window !== 'undefined') {
window.$ = w.jQuery = jQuery
}
})()
// $()--->jQuery的实例
发布订阅设计模式 publish&subscribe
自定义事件的一种方案 灵感来源于 addEventListener DOM2事件绑定,
- 给当前元素的某一个事件行为,绑定多个不同的方法(事件池机制)
- 当事件行为触发的时候,会依次通知事件池中的方法执行
- 支持内置事件(标准事件,)例如 click dbclick mouseenter...
扫盲:dom0级事件绑定原理:给元素对象对应的事件行为的私有属性赋值,dom2往事件池里添加方法
简单版的发布订阅
弊端:只有一个事件池,只能应用于一个场景
(function () {
//自己创造的事件池
let pond = [];
//向事件池中注入方法
function subscribe(func) {
//去重处理
if (!pond.includes(func)) {
pond.push(func)
}
//每一次执行,返回的方法是用来移除当前这个新增的这个方法的
return function unsubscribe() {
pond = pond.filter(item => item !== func)
}
}
//通知事件池中的方法执行
subscribe.fire = function fire(...params) {
pond.forEach(item => {
if (typeof item === 'function') {
item(...params)
}
})
}
window.subscribe = subscribe
})();
let unsubscribe1 = subscribe(function () {
console.log(1, arguments);
});
subscribe(function () {
console.log(2, arguments);
});
subscribe(function () {
console.log(3);
unsubscribe1();
});
subscribe(function () {
console.log(4);
});
setTimeout(() => {
subscribe.fire(10, 20, 30)
}, 1000)
setTimeout(() => {
subscribe.fire(10, 20, 30)
}, 2000)
应用场景:凡是某个阶段到达的时候,需要执行很多方法(更多的时候,到底执行多少个方法不确定,需要边写业务,边处理),我们可以基于发布订阅模式来管理代码:
- 创建事件池->发布计划
- 向事件池中加入方法-->向计划表中订阅任务
- fire-->通知计划表中的任务执行
2.一个项目中,我们可能会出现多个事情都需要基于发布订阅来管理,一个事件池不够
@1管理多个事件池
基于面向对象 类(subscribe,unsubscribe,fire公用的)&实例,(每个实例都有一个自己私有的事件池)
class Sub {
//实例私有的属性:私有的事件池
pond = [];
//原型上设置方法:向事件池中订阅任务
subscribe(func) {
let self = this,
pond = self.pond;
if (!pond.includes(func)) pond.push(func);
//每一次执行,返回的方法是用来移除当前这个新增的这个方法的
return function unsubscribe() {
let i = 0,
len = pond.length,
item = null;
for (; i < len; i++) {
item = pond[i];
if (item === func) {
pond.splice(i, 1)
break;
}
}
}
};
//通知当前实例所属事件池中的方法执行
fire(...params) {
let self = this,
pond = self.pond;
pond.forEach(item => {
if (typeof item === 'function') {
item(...params)
}
})
};
}
let sub1 = new Sub;
sub1.subscribe(function () {
console.log(1, arguments);
})
sub1.subscribe(function () {
console.log(2, arguments);
})
setTimeout(() => {
sub1.fire(100, 200)
}, 1000)
let sub2 = new Sub;
sub2.subscribe(function () {
console.log(1, arguments);
})
sub2.subscribe(function () {
console.log(2, arguments);
})
setTimeout(() => {
sub2.fire(200, 300)
}, 2000)
@2一个事件池支持不同的事件池类型
//type事件类型, func方法
let sub = (function () {
let pond = {};
//向时间池中追加指定自定义事件类型的方法
const on = function on(type, func) {
//验证增加的时候,验证当前类型在事件池中是否已经存在
!Array.isArray(pond[type]) ? pond[type] = [] : null;
let arr = pond[type];
if (arr.includes(func)) return;
arr.push(func);
};
//从事件池中移除指定自定义事件类型的方法
const off = function off(type, func) {
let arr = pond[type],
i = 0,
item = null;
if (!Array.isArray(arr)) throw TypeError(`${type} 自定义事件类型并不存在`);
for (; i < arr.length; i++) {
item = arr[i];
if (item == func) {
//移除掉
//splice改变原数组,导致数组塌陷(删除数组索引发生了变化,在循环会导致这个问题)
// arr.splice(i, 1)
arr[i] = null;//这样只是让集合中当前项值变为null,但是集合中的结构不会发生变化(索引不变),下次执行emit的时候,遇到当前项是null,我们在去把其移除掉即可
break;
}
}
};
//通知事件池中定自定义事件类型的方法执行
const emit = function emit(type, ...params) {
let arr = pond[type],
i = 0,
item = null;
if (!Array.isArray(arr)) throw TypeError(`${type} 自定义事件类型并不存在`);
for (; i < arr.length; i++) {
item = arr[i];
if (typeof item === 'function') {
item(...params)
continue;
}
//不是函数把它移除掉即可
arr.splice(i, 1);
i--;
}
};
return {
on,
off,
emit
};
})()
const fun1 = () => console.log(1)
const fun2 = () => {
console.log(2)
sub.off('A', fun1)
}
const fun3 = () => console.log(3)
const fun4 = () => console.log(4)
const fun5 = () => console.log(5)
const fun6 = () => console.log(6)
sub.on('A', fun1);
sub.on('A', fun2);
sub.on('A', fun3);
setTimeout(() => {
sub.emit('A')
}, 1000)
sub.on('B', fun4);
sub.on('B', fun5);
sub.on('B', fun6);
setTimeout(() => {
sub.emit('B')
}, 2000)
setTimeout(() => {
sub.emit('A')
}, 3000)
观察者模式
目标:管理观察者: 增加/删除观察者 通知观察者中update方法执行(且传递消息notfily)
每一个观察者有update方法接收传的消息并处理
//定义观察者,形式可以不一样,只需要具备update方法
class OB {
update(msg) {
console.log(`我是观察者1,我接受到的信息是${msg}`);
}
}
let DEMO = {
update(msg) {
console.log(`我是观察者2,我接受到的信息是${msg}`);
}
};
//目标:管理观察者: 增加/删除观察者 通知观察者中update方法执行(且传递消息notfily)
class Subject {
observerList = [];
add(observer) {
this.observerList.push(observer)
};
remove(observer) {
this.observerList = this.observerList.filter(item => item !== observer)
};
notify(...params) {
this.observerList.forEach(item => {
if (item && typeof item.update === "function") {
item.update(...params)
}
})
}
}
let sub = new Subject;
sub.add(new OB)
sub.add(DEMO)
setTimeout(() => {
sub.notify('hello word')
}, 1000);
中介者模式
let mediator = (function () {
let topics = [];
const subscribe = function subscribe(callback) {
topics.push(callback)
};
const publish = function publish(...params) {
topics.forEach(callback => {
if (typeof callback === 'function') {
callback(...params)
}
})
};
return {
subscribe,
publish
}
})()
mediator.subscribe(() => console.log(1))
mediator.subscribe(() => console.log(2))
setTimeout(() => {
mediator.publish()
}, 1000)