JavaScript设计模式实战指南
最编程
2024-08-06 14:26:11
...
JS中常见的设计模式
- 单例设计模式 (Singleton)
- 命令模式(Command)
- Constructor构造器模式
- 工厂模式
- 发布订阅模式Public& Subscribe
- 观察者模式Observer
- 中介者模式Mediator
JS设计模式:是一种思想,更规范更合理的去管理代码(方便维护、升级、扩展、开发)
单例设计模式 (Singleton)
- 最早期的模块化编程思想(同样的还有:AMD / CMD / CommonJS / ES6Module )
- 避免全局变量的污染
- 实现模块之间的相互调用(提供了模块导出的方案)
//公共板块
let utiles = (function(){
function debounce(func, wait){};
//...
return {
debounce:debounce,
}
})()
//A板块
let AModule = (function(){
utiles.debounce();
function fn(){};
function jquery(){};
return {
jquery:jquery
}
})()
//B板块(实现当前模块下需要完成的所有的功能)
let BModule = (function(){
utiles.debounce();
AModule.jquery();
function fn(){};
return {}
})()
命令模式(Command)
let BModule = (function(){
utiles.debounce();
AModule.jquery();
//获取数据
function getData(){}
//绑定数据
function binding(){}
//处理事件绑定
function handle(){}
//处理其他事情
function fn(){};
return {
init(){ //模块的入口(相当于模块的大脑,控制模块中方法的执行顺序,这种方式称为命令模式)
getData();
binding();
handle();
fn();
}
}
}
BModule.init();
Constructor构造器模式
- 自定义类和实例
- 私有&公有属性和方法
- 编写公共的类库 & 歇一歇插件组件
*每次调用插件都是创造这个类的一个实例,既保证每个实例之间(每次调用之间)有自己的私有属性,互不影响;也可以保证一些属性方法还是公用的,有效避免代码的冗余…
//实现方法一:
function Fn(){
//原型上的属性
this.xxx = xxx;
}
Fn.prototype = {
construtor:Fn,
//原型上的方法
query(){},
//...
};
//私有的方法
Fn.xxx = function () {};
new Fn();
//实现方法二:
class Fn2{
constructor(){
};
//原型上的方法
query(){};
//私有的方法
static xxx(){}
}
let f1 = new Fn2();
let f2 = new Fn2();
工厂模式
- 简单的工厂模式:一个方法根据传参的不同,做了不同事情的处理
- JQ中的工厂模式:加工转换
- 经典案例:后台开发的时候,会有一个产品需要适配多套数据库(MySQL sqlserver Oracle),项目需要根据一些配置,转换到对应的数据库上
//简单的工厂模式
function factory(options){
if(options == null) options ={};
if(!/^(function| object)$/i.test(typeof options) ) options = {};
let {
type,
payload
} = options;
if(type === "MYSQL"){
// ...
return;
};
if(type === "SQLSERVER"){
// ...
return;
};
//...
}
factory({
type: "SQLSERVER",
root:"",
pass:"",
select:""
});
(function(){
function jQuery(selector, context){};
jQuery.fn = jQuery.prototype = {
constructor:jQuery,
//...
}
//中间件转换
function init (){};
jQuery.fn.init = init;
init.prototype = jQuery.fn;
if(typeof window !== "undefined"){
window.$ = window.jQuery = jQuery;
}
})()
发布订阅模式Public& Subscribe
灵感来源于 addEventListener DOM2 事件绑定
- 给当前元素的某一个事件行为,绑定多个不同的方法(事件池机制)
- 事件行为触发,会依次通知事件池中的方法执行
- 支持内置事件(标准事件:click、dbclick、mouseenter…)
- 应用场景:凡事某个阶段到达的时候,需要执行很多方法(更多时候,到底执行多少方法不确定,需要边写业务边处理),我们都可以基于发布订阅设计模式来管理代码;(创建事件池->发布任务)|(向事件池中加入方法 ->向计划表中订阅任务)|( fire -> 通知计划表中任务执行 )
(function(){
//自己创造的事件池
let pond = [];
//向事件池中注入方法
function subscribe(func){
//去重处理
if(pond.include(func)) return ;
pond.push(func);
//每一次执行,返回的方法是用来移除这一项的
return function unsubscribe(){
pond = pond.filter(item =>item !==func);
/*等于
if(item ===func){return false};
return true;
*/
}
};
//通知事件池中所有的事件执行
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);
//需求:从服务器获取数据,获取数据后要干很多事情
//A
const f1 = data=>{};
subscribe(f1);
//B
const f2 = data=>{};
subscribe(f2);
//C
const f3 = data=>{};
subscribe(f3);
//D
const f4 = data=>{};
subscribe(f4);
//M
query.then(data=>{
subscribe.fire(data);
})
一个项目中可能会出现多个事情需要基于发布订阅来管理,一个事件池不够
- 管理多个事件池
- 面向对象:类和实例
- 每个实例都有一个自己私有的事件池
- subscribe/ unsubscribe/fire是公用的
- 一个事件池支持不同的自定义事件类型
//基于jquery 的$.callbacks() 实现
let $plan1 = $.Callbacks();
//add remove fire
$plan1.add(function(){console.log(1, arguments)})
$plan1.add(function(){console.log(2, arguments)})
setTimeout(function(){
$plan1.fire(100,200)
},1000)
let $plan2 = $.Callbacks();
$plan2.add(function(){console.log(3, arguments)})
$plan2.add(function(){console.log(4, arguments)})
setTimeout(function(){
$plan2.fire(300,400)
},2000)
运行结果:
clss Sub {
//实例私有的属性:私有的事件池
pond = [];
//原型上设置方法
//1.向当前实例所属事件池中订阅任务
subscribe(func){
let self = this,
pond = self.pond;
if(!pond.includes(func)) pond.push(func);
return function unsubscribe(){
//pond = pond.filter(item =>item !==func);
let i = 0,
len = pond.length,
item = null;
for(; i <len;i++){
item = pond[i];
if(item === func){
pond.splice(i,1);
break;
}
}
}
}
//3. 通知当前实例所属事件池中的任务执行
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(function(){ sub1.fire(100,200)},1000)
let sub2 = new Sub;
sub2.subscribe(function(){console.log(3, arguments)})
sub2.subscribe(function(){console.log(4, arguments)})
setTimeout(function(){ sub2.fire(300,400)},2000)
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,
len = arr.length;
if(!Array.isArray(pond[type])) throw new TypeError(`${type}自定义事件在事件池中并不存在`);
for(;i<len;i++){
item = arr[i];
if(item === func) i{
//移除掉
//arr.splice(i,1);
arr[i] =null;
break;
}
}
};
//通知事件池中指定自定义类型的方法执行
const emit = function emit(type, ..params){
let arr = pond[type],
i = 0,
item = null,
len = arr.length;
if(!Array.isArray(pond[type])) throw new TypeError(`${type}自定义事件在事件池中并不存在`);
for(;i<len;i++){
item = arr[i];
if(typeof item === "function"){
item(...params);
continue;
}
arr.splice(i,1);
i--;
}
};
return {
on,
off,
emit
};
})();
const fn1 = ()=> console.log(1);
const fn2 = ()=> console.log(2);
const fn3 = ()=> console.log(3);
const fn4 = ()=> console.log(4);
sub.on("A",fn1);
sub.on("A",fn2);
setTimeout(function(){ sub.emit("A")},1000);
sub.on("B",fn3);
sub.on("B",fn4);
setTimeout(function(){ sub.emit("B")},2000);
/*
1
2
3
4
*/
观察者模式Observer
发布订阅是观察者模式的升级版
- 首先要有一个目标容器
- 增加(add )观察者
- 删除(remove)观察者
- 通知观察者中的update执行并且传递消息(notify)
- 有n个观察者,每个观察者都有一个update方法
// 定义观察者:形式可以不一样,只需要具备update方法即可
class OBSERVER {
update(msg) {
console.log(`我是观察者1,我接收到的消息是:${msg}`);
}
}
let DEMO = {
update(msg) {
console.log(`我是观察者2,我接收到的消息是:${msg}`);
}
};
// 目标
class Subject {
observerList = [];
add(observer) {
if(!observerList.includes(observer)) observerList.push(observer);
};
remove(observer) {
let i = 0,
item = null,
len = observerList.length;
if(!observerList.includes(observer)) throw new TypeError(`${type}自定义事件在事件池中并不存在`);
for(;i<len;i++){
item = observerList[i];
if(item === observer){
observerList[i] =null;
break;
}
}
};
notify(...params) {
let i = 0,
item = null,
len = observerList.length;
if(len<1)) return;
for(;i<len;i++){
item = observerList[i];
if(typeof item === "function") {
item(...params);
continue;
};
observerList.splice(i,1);
i--;
}
};
}
let sub = new Subject;
sub.add(new OBSERVER);
sub.add(DEMO);
setTimeout(() => {
sub.notify('hello world~~');
}, 1000);
中介者模式Mediator
// 中介者模式
let mediator = (function () {
let topics = [];
const subscribe = function subscribe(callback) {
if(!topics.includes(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);
上一篇: 探究JDBC中的设计模式
下一篇: Java中的策略模式详解(第十四篇)