前端常用的设计模式

转载自:https://zhuanlan.zhihu.com/p/115874575

一、常用设计模式

1、单例模式:确保只有一个实例,并提供全局访问。

2、策略模式:定义一些列的算法,把它们一个个封装起来,并且使它们可以相互替换。

3、代理模式:为一个对象提供一个代用品或占位符,以便控制对它的访问。js里虚拟代理(网络请求方面)、缓存代理(数据方面)最常用

4、迭代器模式:提供一种方法,顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。不需要关心对象的内部构造,也可以按顺序访问其中的每个元素。很多语言都有自己内置的迭代器,比如js的Array.prototype.forEach

5、发布-订阅模式:又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态生改变时,所有依赖于它的对象都将得到通知。在js中,一般用事件模型代替它。

var a={} a.b={} a.c=function(key,fn){   if(!this.b[key]){     this.b[key]=[]      }   this.b[key].push(fn) } a.d=function(){   var key =Array.prototype.shift.call(arguments),       fns=this.b[key]     console.log(arguments)   if(!fns||fns.length===0){     return false;      }   for(var i=0,fn;fn=fns[i++];){     fn.apply(this,arguments)   } } a.c('88',function(pri){     console.log(pri) }) a.c('110',function(pri){     console.log(pri) }) a.d('88',200) a.d('110',300)

6、命令模式:执行某些特定事情的指令。记录信息的清单,就是命令模式中的命令对象。

PS:有时候,需要像某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么。这时候就需要命令模式,使得请求发送者和请求接收者能够消除耦合关系。

7、组合模式:使用树形方式创建对象的结构,把相同的操作应用在组合对象和单个对象上。

8、模板方法模式:只需要集成就可以实现,由两部分组成,第一部分是抽象类,第二部分是具体的实现子类。通常在抽象父类中封装了子类的算法框架,包括实现一些公共方法以及封装子类中所有方法的执行顺序。子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法。

//抽象类 var a=function(){}; a.prototype.b=function(){     console.log('把水煮沸') } a.prototype.c=function(){} a.prototype.d=function(){} a.prototype.e=function(){} a.prototype.init=function(){     this.b();     this.c();     this.d();     this.e(); } //子类 var g=function(){} g.prototype.c=function(){     console.log('用沸水冲咖啡') } g.prototype.d=function(){     console.log('把咖啡倒进杯子') } g.prototype.e=function(){     console.log('加糖和牛奶') } var h=new g() h.init()
这里的a.prototype.init就是模板方法

9、好莱坞原则:即高层组件调用底层组件,模板方法是好莱坞的一个典型使用场景;子类放弃了对自己的控制权,而是改为父类通知子类哪些方法应该在什么时候被调用。作为子类,只负责提供一些设计上的细节。

Ps:发布-订阅模式、回调函数都用到了此原则

10、享元模式:运用共享技术来有效支持大量细粒度的对象。

Ps:

  1. 内部状态存储与对象内部。
  2. 内部状态可以被一些对象共享。
  3. 内部状态独立于具体的场景,通常不会改变。
  4. 外部状态取决于具体的场景,并根据场景而变化,外部状态不能被共享。

11、对象池:维护一个装在空闲对象的池子,如果需要对象的时候,不是直接new,而是转从对象池里获取。如果对象池里没有空闲对象,则创建一个新的对象,当获取出的对象完成它的职责后,再进入池子等待被下次获取;

12、职责链模式:使多个对象都有机会处理请求,并从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

PS:有多个if-else条件的时候,要去考虑是否可以用职责链模式。

13、中介者模式:解除对象与对象之间得耦合关系。增加一个中介者对象后,所有得相关对象都通过中介者对象来通信,而不是互相引用,所有当一个对象发生改变时,只需要通知中介者对象即可。此模式迎合迪米特法则的一种实现,迪米特也叫做最少知识原则,是指一个对象应该尽可能少地了解另外的对象。

PS:编写思路:1、利用发布-订阅模式2、在中介者对象中开放一些接收消息得接口

14、装饰者模式:给对象动态地增加职责。这种方式并没有真正地改动对象自身,而是将对象放入另一个对象之中,这些对象以一条链的方式进行引用,形成一个聚合对象。

PS:经常用到Function.pototype.after和Function.pototype.before两个函数进行装饰

Function.prototype.before=function(beforefn){      var _self=this;//保存原函数引用 return function(){// 返回包含了原函数和新函数的“代理”函数          beforefn.apply(this,arguments);// 执行新函数,且保证this不被劫持,新函数接受的参数也会被原封不动地传入原函数,新函数在原函数之前执行 return _self.apply(this,arguments);// 执行原函数并返回原函数的执行结果,并且保证this不被劫持      }  }   

注意:这里的beforefn和原函数_self共用一组参数列表arguments,当我们在beforefn的函数体内改变arguments的时候,_self接收的参数列表自然也会变化,所以经常用于动态地改变原函数的参数

15、状态模式:关键是区分事物内部的状态,事物内部状态的改变往往会带来事物的行为改变。此模式关键是把事物的每种状态都封装成单独的类。

PS:状态模式和策略模式区别:

策略模式的各个策略类之间是平等又平行的,他们之间没有任何联系,所以客户必须熟知这些策略类的作用。以便随时切换算法;而状态模式,状态和状态对应的行为早已被封装好的,状态之间的切换也早被规定完成,“改变行为”这件事发生在状态模式内部。对客户来说,并不需要了解这些细节,这正是状态模式的作用所在。

16、适配器模式:解决两个软件实体的接口不兼容的问题。不考虑接口是怎样实现的,也不考虑将来可能会如何让变化,适配器模式不需要改变已有的接口,就能够使他们协同作用,适配器模式通常只包装一次。

var a={     show:function(){         console.log('a数据源开始渲染')     } } var b={     disply:function(){         console.log('b数据源开始渲染')     } } //适配器c var c={     show:function(){         return b.disply();     } } var render=function(fn){     fn.show() } render(a) render(c)

17、外观模式:在JS中不常用。主要是为子系统中的一组接口提供一个一致的界面,定义了一个高层的接口,这个接口使子系统更加容易使用。在C++或者JAVA中指的是一组类的集合,这些类相互协作可以组成系统中一个相对独立的部分。但在JS中,这个子系统至少应该指的是一组函数的集合。

var A=function(){     a1()     a2() }
posted @ 2021-12-10 17:59  liuxu_xrl  阅读(545)  评论(0编辑  收藏  举报