JS中常见的设计模式

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);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值