设计模式就是一种思想,代码整洁,易懂,维护,扩展,优化
单例模式
单例模式:基于单独的实例 管理某一个模块内容 从而实现模块的独立划分 也可以实现相互调用
- 类只有一个实例
- 全局可访问该实例(咱们这里利用闭包和函数作用域)
当项目开始的时候,划分模块,一人一个模块
var A = (function(){
var data = [];
function change(val){
data.push(val)
}
function handleClick(){ }
}
return {
change
}
)()
// 通过A 暴露出的方法,来实现相互调用
var B = (function(){
var data = [];
function handleClick(){ }
}
A.change(11);
return {
handleClick
}
)()
这种的模式的缺点也显而易见,大家都在同一个闭包下面,如果修改额话会影响到当前闭包内的东西,如果一旦想私有化,每个都有自己的容器就不好搞了
A.change(10)
A.change(20)
在业务开发中大家肯定遇过这种情况,需要先执行 1, 在执行 2, 在执行 3,是相互依赖关系,这时侯随之产生的
命令模式
咱们就拿单例 + 命令模式看下
- 根据不同的命令来执行不同的操作
let SearchModule = (
function(){
function QueryData () {};
function bindHTML () {};
}
return {
// 命令模式: 就相当于控制器,控制谁先执行,谁后执行 !
init: function(){
QueryData();
bindHTML();
}
}
)()
SearchModule.init()
咱们上面说的单例模式的缺点:这时候可以再看一把:
构造器模式
每个实例都属于自己,也就是有单独的一个空间,互相独立,有自己的私有属性和方法 or 公共方法
类 & 实例
私有 & 公有属性方法
class A {
constructor() {
// this = 每个类实例
this.arr = [];
}
change(val){
this.arr.push(val)
}
// 如果使用的是ES5如果放在函数内
// 每new一次就需要重新创建,可以直接挂载到原型
prompt(){
console.log(this.arr)
}
}
A.prototype.prompt = function (){
console.log(this.arr)
}
var a1 = new A;
var a2 = new A;
console.log(a1 === a2) false
console.log(a1.arr === a2.arr) false
console.log(a1.change === a2.change) true // 因为都挂到原型上去了所以调用的是同一个方法
工厂模式
核心思想:中转站
可以帮助我们实现调用的切换 or 中转处理, 统一处理,流水线
function tory( options ){
options = options || {};
let { type, payload, } = options;
if(type === 'array'){
//处理一些逻辑
return
}else {
// 在处理另外的逻辑
return
}
}
观察者模式(vue2.0响应式原理)
目标一旦发生变化就会通知观察者做出相应的动作
上图:图 结合 代码 缕下思路
// 观察者
class Obsever{
update(message) {
// 消息送达,通知update进行
console.log(message)
}
}
// 观察者
class Demo{
update(message) {
// 消息送达,通知update进行
console.log(message)
}
}
//目标
class Subject {
// 当然你也可以把这些方法抽离出去做一个观察者管理专属类,通过new 来调用
constructor(){
this.observerList = [];
}
add(observer){
//可以做把去重
this.observerList.push(observer)
return this;
}
remove(){
this.observerList = this.observerList.filter(ob => ob !== observer)
return this;
}
get(index){
// 判断符合不符合要求
return this.observerList[index]
}
count(){
return this.observerList.length;
}
.........
// 通知
notify(...params) {
// 为什么用for循环,因为for循环相比与forEach性能要好
for(let i=0;i<this.count(); i++){
let item = this.get(i);
item.update(...params)
}
}
}
let sub = new Subject;
sub.add(new Obsever);
sub.add(new Demo);
setTimtout(() => {
sub.notify('公司新品上市,欢迎大家抢购')
},1000)
中介者模式
托管第三方来搞,可以有效降低耦合度
// 由单例模式实现 中介者模式
let mediator = (function(){
let topics = {};
// 订阅: A组件的方法 topic:可以理解为标识
let subscribe = function subscribe(topic,callback){
!topics[topic] ? topics[topic] = [] : null;
topics[topic].push({
context: this,
callback
})
},
// 发布: B组件通知之前订阅的方法
let publish = function publish(topic,...params){
if(!topics[topic]) return;
topics[topic].forEach( item => {
let { callback,context } = item;
callback.call(context,...params)
})
}
return {
subscribe,
publish
}
})()
发布订阅模式
个人认为发布订阅模式很类似于 DOM2级事件 addEventListener
-
给当前元素的某个事件绑定上方法 也算一个 事件池 机制
-
事件行为触发,会依次通知事件池中的方法执行
-
必须是内置事件(支持事件) 应用场景:某个阶段到达的时候,需要执行很多方法,都可以基于该模式

用单例模式实现的话:
(function(){
let pond = [];
// 向事件池中注入自定义事件
function subscribe(func){
//可以做一些去重判断
if(!pond.includes(func)) {
pond.push(func)
};
// 同时做一个,移除当前新增的方法
return function unSubscribe(){
pond = pond.filter( itm => itm !== func)
}
}
subscribe.fire = function(){
pond.forEach(itm => {
if(typeof itm === "function"){
itm()
}
})
}
window.subscribe = subscribe;
})()
let unsubscribe = subscribe(function(){console.log(111)})
subscribe(function(){console.log(22222)})
subscribe(function(){console.log(333333),unsubscribe()})
subscribe(function(){console.log(444444)})
subscribe(function(){console.log(555555)})
setTimeout(() => {
subscribe.fire()
},1000);
setTimeout(() => {
subscribe.fire()
},3000)
上面介绍完ES5的版本,有个缺点式只有一个事件池,如果需要多个事件池的话就力不从心了,所以这时候咱们的需求式要支持多个事件池
那么 ES6面向对象来了
class Sub {
pond = [];
// 原型上设置方法
subscribe(func){
let self = this;
pond = self.pond;
if(!pond.includes(func)) this.pond.push(func)
return function unsubscribe(){
// 可以用filter
let i = 0;
let len = pond.length;
for(;i<len;i++){
if(pond[i] === func){
pond.splice(i,1)
break
}
}
}
}
fire(...params){
let self = this;
self.pond.forEach(itm => {
if(typeof itm === 'function){
itm(...params)
}
})
}
}
当你看到这你可能很疑惑这个和 addEventListener 根本不太一样啊
好,接下来我基于上面单例写法扩展下,也可以基于ES6搞一下
let sub = ( function(){
pond = {};
// 原型上设置方法
const on = (type,func) => {
//首先判断pond中有没有该项,没有创建一个数组
!Array.isArray(pond[type]) ? pond[type] = [] : null;
let arr = pond[type];
if(arr.includes(func)) return ;
arr.push(func);
}
const remove = (type,func) => {
let arr = pond[type];
let i = 0;
item = null;
for(;i<arr.length; i++){
if(item === func){
//不能直接移除,否则会造成数组塌陷问题,赋值null就好,下次执行前filter为null的
arr[i] = null;
break;
}
}
}
const fire = (type,...params) => {
let arr = pond[type];
//也可以判断是不是数组
i = 0;
item = null;
for(;i < arr.length; i++){
item = arr[i];
if(typeof itm === 'function'){
itm(...params);
continue;
}
// 将不是函数的都移除掉(包含null) 解决数组塌陷的i--
arr.splice(i,1);
i--
}
}
return {
on,
remove,
fire
}
})()
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)
sub.on('B',fn3)
sub.on('B',fn4)
setTimeout(() => {
sub.fire('A',1)
},1000)
后续继续更新