创建型模式:用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。包括简单工厂模式、工厂方法模式、抽象工厂模式、单例模式、生成器模式、原型模式 5 种。

结构型模式:用于描述如何将类或对象按某种布局组成更大的结构。包括代理模式、适配器模式、桥接模式、装饰器模式、外观模式、享元模式、组合模式  7 种。

行为型模式:用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责。模板方法、策略模式、命令模式、职责链模式、状态模式、观察者模式、中介者模式、迭代器模式、访问者模式、备忘录模式、解释器模式 11种。

【工厂模式】:

  • 工厂模式是创建对象常见的设计模式,又叫做静态工厂模式,它把创建对象的具体逻辑进行封装,由一个工厂对象决定创建某一个类的实例。
  1. 调用者创建对象时只要知道其名称即可
  2. 扩展性高,如果要新增一个产品,直接扩展一个工厂类即可。
  3. 隐藏产品的具体实现,只关心产品的接口。
  • 应用场景:需要依赖具体环境创建不同的实例,这些实例都有相同的行为,这时候我们可以使用工厂模式,简化实现的过程,同时也可以减少每种对象所需的代码量,有利于消除对象间的耦合,提供更大的灵活性;将new操作简单封装,遇到new的时候就应该考虑是否用工厂模式;
<script>// 定义一个Person类function Person(name){this.name = name}Person.prototype.getName = function(){console.log(this.name);}// 定义一个Car类function Car(model){this.model = model}Car.prototype.getModel = function(){console.log(this.model);}// 构造function create(type,param){return new this[type](param)}create.prototype = {person:Person,car:Car}// 实例化var person1 = new Create('person','zhangsan')person1.getName()</script>

【抽象工厂模式】:

  • 希望在不改变原对象的基础上,通过对其拓展功能和属性来实现更复杂的逻辑。
  • 抽象工厂模式包含如下 4 种角色:抽象工厂、具体工厂、抽象产品、具体产品。

  • 使用场景:继承同一父类、实现同一接口的子类对象,由给定的多个类型参数创建具体的对象。

如汽车厂商不但能生产 Car,也可以生产发动机 Engine,这就是两个产品等级结构,一个产品族。再下面例子中,如果我们需要新增一个Audi 厂商,我们只需要新增一个具体的AudiFactory工厂和一个具体产品的AudiCar 和 AudiEngine类,然后进行实例化,便可以实现。

 <script>// 定义生产汽车的抽象工厂class makeCarFactory {//生产汽车createCar() {throw new Error('不能调用抽象方法,请自己实现');}//生产汽车发动机createEngine() {throw new Error('不能调用抽象方法,请自己实现');}}// 定义具体生产工厂,实现抽象工厂class BenzFactory extends makeCarFactory {createCar() {return new BenzCar();}createEngine() {return new BenzEngine();}}// 定义抽象产品类Carclass Car {dirve() {throw new Error('不能调用抽象方法')}}// 定义具体产品类BenzCarclass BenzCar extends Car {dirve () {console.log('Benz drive');}}// 定义抽象产品Engineclass Engine {start() {throw new Error('不能调用抽象方法')}}// 定义具体产品类BenzEngineclass BenzEngine extends Engine {start() {console.log('Benz engine start');}}// 实例化let benz = new BenzFactory();let benzCar = benz.createCar();let benzEngine = benz.createEngine();benzCar.dirve(); //Benz drivebenzEngine.start();  //Benz engine start</script>

【建造者模式】:

  • 建造者模式(Builder)是一种比较复杂使用频率较低的创建型设计模式,也叫生成器模式,主要用于将一个复杂对象的构建与他的表现分离,使得同样的构建过程可以创建不同的表示。
  • 例如生产汽车,分步骤创建安装不同的零件。如果创建逻辑简单则没有拆分的必要。
  • 使用场景:需要将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示的意图;创建时有很多必填参数需要验证;创建时参数求值有先后顺序、相互依赖;创建有很多步骤,全部成功才能创建对象
<script>
class Programmer {age: numberusername: stringconstructor(p) {this.age = p.agethis.username = p.username}toString() {console.log(this)}
}class Builder {age: numberusername: stringbuild() {if (this.age && this.username && this.color) {return new Programmer(this)} else {throw new Error('缺少信息')}}setAge(age: number) {if (age > 18 && age < 36) {this.age = agereturn this} else {throw new Error('年龄不合适')}}setUsername(username: string) {if (username !== '小明') {this.username = usernamereturn this} else {throw new Error('小明不合适')}}
}const p = new Builder().setAge(20).setUsername('小红').build().toString()</script>

【单例模式】:

  • 我们无论创建多少次对象都指向同一个。
  • 使用场景:线程池、全局缓存、浏览器中的window对象。
  • 例子:当点击页面的登录案例,会弹出登录弹窗,这个弹窗无论点击多少次只能被创建一次,那么这个弹窗就可以用单例模式创建。
<body><button id="btn">点击</button><div style=""></div><img src="" alt=""><script>// 单例构造函数function Person(name) {this.name = name}// 获取实例的名字Person.prototype.getName = function () {console.log(this.name);}//创建实例var CreateSinglePerson = (function () {// 定义instance,用来表示person实例是否已经创建var instance;return function (name) {if (!instance) {instance = new Person(name)}return instance}})()var a = new CreateSinglePerson('a')var b = new CreateSinglePerson('b')console.log(a === b);  //true  说明每次实例的对象都是同一个// -----------------------------------------------// 遮罩层和弹出框var singleton = function (fn) {var instance;return function () {return instance || (instance = fn.apply(this, arguments))}}// 创建遮罩层var Mask = function () {// 创建div元素var mask = document.createElement('div')// 设置样式mask.style.position = 'fixed'mask.style.top = '0'mask.style.right = '0'mask.style.bottom = '0'mask.style.left = '0'mask.style.opacity = '0.75'mask.style.backgroundColor = '#000'mask.style.display = 'none'mask.style.zIndex = '999'document.body.appendChild(mask)// 单击隐藏遮罩层mask.onclick = function () {this.style.display = 'none'}return mask}// 创建登录弹窗var Login = function () {var login = document.createElement('div')login.style.position = 'fixed'login.style.left = '50%'login.style.top = '50%'login.style.zIndex = '1000'login.style.display = 'none'login.style.padding = '50px 80px'login.style.backgroundColor = '#fff'login.style.border = '1px solid #ccc'login.style.borderRadius = '6px'login.innerHTML = '登录'document.body.appendChild(login)return login}document.getElementById('btn').onclick = function () {var Omask = singleton(Mask)()Omask.style.display = 'block'var Ologin = singleton(Login)()Ologin.style.display = 'block'var w = parseInt(Ologin.clientWight)var h = parseInt(Ologin.clientHight)}</script>
</body>

【原型模式】:

  • 原型模式解决了方法或者属性不能共有的问题,在原型模式中,把实例之间相同的属性和方法提取成共有的属性和方法,即:想让谁共有,就把它放在 类.prototype上。
  • 使用场景:原型模式是基于已有的对象克隆数据,而不是修改原型链;创建对象的代价太大,而同类的不同实例对象属性值基本一致,通过原型克隆的方式可以节约资源;不可变对象可以用浅克隆实现,可变对象用深克隆实现,深克隆占用资源多;同一对象不同时间版本,可以对比没变化的浅克隆,变化的深克隆,然后新版本替换旧版本。
<script>function CreatePerson(name,age) {this.name = name,this.age = age;}CreatePerson.prototype.wirte = function() {console.log(this.name + 'write');}var p1 = new CreatePerson('wang', 18);var p2 = new CreatePerson('ling', 15);p1.wirte();
</script>

【装饰器模式】:

  • 希望在不改变原对象的基础上,通过对其拓展功能和属性来实现更复杂的逻辑。
  • 使用场景:装饰器类是对原始功能的增强;装饰器类和原始类继承同样的父类,这样我们可以对原始类嵌套多个装饰器类;主要解决继承关系过于复杂的问题,通过组合来替代继承;可以通过对原始类嵌套使用多个装饰器。
<script>//生产手机class Cellphone {create() {console.log('生成手机')}}// 装饰器装饰生产手机的同时生产手机壳class Decorator {constructor(cellphone) {this.cellphone = cellphone}create() {// 生产手机this.cellphone.create()// 生产手机壳this.createShell(cellphone)}createShell() {console.log('生成该类型手机的手机壳')}}let cellphone = new Cellphone()cellphone.create() // 只能生产手机let newPhone = new Decorator(cellphone)newPhone.create() // 生产该手机的手机壳
</script>

【组合模式】:

  • 组合模式作用于将多个部分通过组合变成一个整体。
  • 通过对象的多态表现,使得用户对单个对象和组合对象的使用具有一致性。
  • 使用场景:将一组对象组织成树形结构,以表示一种 “部分-整体 ”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性(递归遍历)。

例1、

<script>// 单个指令var closeDoorCommand = {excute:function(){console.log('关门');}// excute() {//  console.log('关门');// }}var openPcCommand = {excute: function () {console.log('打开电脑');}}var openQQCommand = {excute: function () {console.log('登录QQ');}}// 调用者var MacroCommand = function () {return {commandsList: [],// 把每一个指令放进去add: function (command) {this.commandsList.push(command)},// 遍历执行excute: function () {for (var i = 0, command; command = this.commandsList[i++];) {command.excute()}}}}var macroCommand = MacroCommand()macroCommand.add(closeDoorCommand)macroCommand.add(openPcCommand)macroCommand.add(openQQCommand)macroCommand.excute()</script>

例2、

<script>
window.onload = function(){//组合寄生式继承function inheritPrototype(subClass,superClass){function F(){};F.prototype = superClass.prototypesubClass.prototype = new F()subClass.prototype.constructor = subClass}// 容器基类function Container(){this.children = []this.element = null}Container.prototype = {init:function(){throw new Error('请重写init方法')},add:function(child){this.children.push(child)this.element.appendChild(child.element)return this}}// 基于容器基类创建表单容器function CreateFrom(id,method,action,parent){Container.call(this)this.id = id || ''this.method = method || 'get'this.action = action || ''this.parent = parent this.init()}inheritPrototype(CreateFrom,Container)CreateFrom.prototype.init = function(){this.element = document.createElement('from')this.element.id = this.idthis.element.method = this.methodthis.element.action = this.action}CreateFrom.prototype.show = function(){this.parent.appendChild(this.element)}// 创建行容器组件function CreateLine(className){Container.call(this)this.className = className === undefined? 'form-line':'form-line'+classNamethis.init()}inheritPrototype(CreateLine,Container)CreateLine.prototype.init = function(){this.element = document.createElement('div')this.element.className = this.className}// 创建label组件function CreateLabel(text,forName){this.text = text || ''this.forName = forName || ''this.init()}CreateLabel.prototype.init = function(){this.element = document.createElement('label')this.element.setAttribute('for',this.forName)this.element.innerHTML = this.text}// 创建inputfunction CreateInput(type,id,name,defaultValue){this.type = type || ''this.id = id || ''this.name = name || ''this.defaultValue = defaultValue || ''this.init()}CreateInput.prototype.init = function(){this.element = document.createElement('input')this.element.type = this.typethis.element.id = this.idthis.element.name = this.namethis.element.value = this.defaultValue}var form = new CreateFrom('owner-from','GET','./a.html',document.body)var userLine = new CreateLine().add(new CreateLabel('用户名','user')).add(new CreateInput('text','user','user'))var pwdLine = new CreateLine().add(new CreateLabel('密码','pwd')).add(new CreateInput('password','pwd','pwd'))var submitLine = new CreateLine().add(new CreateInput('submit','','','登录'))form.add(userLine).add(pwdLine).add(submitLine).show()
}</script>

【代理模式】:

  • 所谓的的代理模式就是为一个对象找一个替代对象,以便对原对象进行访问。使用代理的原因是我们不愿意或者不想对原对象进行直接操作,我们使用代理就是让它帮原对象进行一系列的操作,等这些东西做完后告诉原对象就行了。

  • 使用场景:给原类添加非功能性需求,为了将代码与原业务解耦;业务系统的非功能性需求开发:监控、统计、鉴权、限流、日志、缓存。

<script>var Flower = function () {}// 小明var xiaoming = {sendFlower: function (target) {// 拿到花var flower = new Flower()// 目标对象收花target.receiveFlower(flower)}}// 代理对象var friend = {receiveFlower: function (flower) {// 接受小明给的花,并给女神nvshen.receiveFlower(flower)}}// 女神var nvshen = {receiveFlower: function (flower) {console.log("我收到了来自小明的花");}}xiaoming.sendFlower(friend)
</script>

【外观模式】:

  • 为子系统的一组接口提供一个一致的界面,定义了一个高层接口,这个接口使子系统更加容易使用。
  • 外观模式很常见,它其实就是通过一个单独的函数,来简化对一个或多个更大型,更为复杂的函数的访问,是一种对复杂操作的封装,通过把复杂操作封装,调用时直接用方法调用。
  • 使用场景:将多个后端接口请求合并为一个,提高响应速度,解决性能问题;通过封装细粒度接口,提供组合各个细粒度接口的高层次接口,来提高接口的易用性。像Ajax封装。
<script>// 就是系统中复杂的逻辑进行抽象与封装,提供一个更简单易用的API// 像JQuery中把原生DOM操作进行抽象与封装,并消除浏览器之间的兼容问题// DOM事件的绑定与取消function addEvent(element, event, handler) {if (element.addEventLister) {element.addEventLister(event, handler, false)} else if (element.attachEvent) {element.attachEvent('on' + event, handler)} else {element['on' + event] = fn}}function removeEvent(element, event, handler) {if (element.addEventLister) {element.removeEventLister(event, handler, false)} else if (element.attachEvent) {element.removeEvent('on' + event, handler)} else {element['on' + ecent] = null}}
</script>

【桥接模式】:

  • 桥接模式将抽象部分与它的实现部分分离,使它们都可以独立地变化。
  • 一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性,一个类存在两个独立变化的维度,且这两个维度都需要进行扩展,避免在两个层次之间建立静态的继承关系,通过桥接模式可以使他们在抽象层建立一个关联关系。比如,那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统。
  • 使用场景:将抽象和实现解耦,让它们可以独立变化;一个类存在多个独立变化的维度,我们通过组合的方式,让多个维度可以独立进行扩展;非常类似于组合优于继承原则。
<script>class Color {constructor(name) {this.name = name;}}class Shape {constructor(name, color) {this.name = name,this.color = color;}draw() {console.log(this.color + '的' + this.name);}}let red = new Color('red');let circle = new Shape('circle', red)circle.draw()
</script>

【享元模式】:

  • 运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,
  • 在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
  • 使用场景:一个程序中使用了大量的相似对象;由于使用了大量对象,造成很大的内存开销;对象的大多数状态都可以变为外部状态;剥离出对象的外部状态之后,可以用相对较少的共享对象取代大量对象。
  • 享元模式要求将对象的属性划分为内部状态和外部状态(状态在这里通常指属性),其目标是尽量减少共享对象的数量。

有个服装厂,生产了男女服装各50种款式,为了推销需要找模特来拍照,正常可能会找男女模特各50个,每个模特分别穿一种服装拍一组照片。但是如果这种,后面款式越来越多,也就意味着模特也越来越多,仔细分析一下,其实不管有多少种类衣服,我们只需要男女各一个模特来穿衣服进行拍照也可实现该需求。

<script>// 构建享元对象class Model {constructor(id, gender) {this.gender = gender,this.name = `王${gender}${id}`;}}// 构建享元工厂class ModelFactory {// 单例模式static create(id, gender) {if(this[gender]) {return this[gender];}return this[gender] = new Model(id, gender);}}// 管理外部状态class TakeClothesManger {// 添加衣服款式static addClothes(id, gender, clothes) {const model = ModelFactory.create(id, gender);this[id] = {clothes,model}}// 拍照static takePhoto(id) {const obj = this[id];console.log(`${obj.model}模特${obj.model.name}穿${obj.clothes}拍了张照`);}}//实例化for(let i=0; i< 50; i++) {TakeClothesManger.addClothes(i, '男', `衣服${i}`);TakeClothesManger.takePhoto(i);}for(let i=0; i < 100; i++) {const {addClothes, takePhoto} = TakeClothesManger;TakeClothesManger.addClothes(i, '女', `衣服${i}`);TakeClothesManger.takePhoto(i);}
</script>

【观察者模式】:

  • 观察者模式又叫发布订阅模式或消息模式。这种模式一般会定义一个主体和众多个的个体,这里的主体可以想象成一个消息中心,里面有各种各样的消息,而众多的个体可以订阅里面不同的消息,当未来消息中心发布某个消息的时候,订阅过他的个体就会得到通知。
  • 使用场景:将观察者与被观察者解耦;发布订阅模式有发布订阅调度中心(中间商),观察者模式没有。

例1、

<script>var msgCenter = (function(){// 存储消息var _msg = {}return {// 订阅消息register:function(type,fn){if(_msg[type]){_msg[type].push(fn)}else{_msg[type] = [fn]}},// 发布消息fire:function(type,args){if(!_msg[type]){return}var params = {type:type,args:args || {}}for(var i=0;i < _msg[type].length;i++){_msg[type][i](params)}},// 取消消息cancle:function(type,fn){if(!_msg[type]){return}for(var i =0;i < _msg[type].length;i++){if(_msg[type][i] === fn){_msg[type].splice(i,1)break}}}}})()function Person(){this.alreadyRegister = {}}Person.prototype.register = function(type,fn){if(this.alreadyRegister[type]){console.log('你已经订阅过这个消息了,请不要重复订阅');}else{msgCenter.register(type,fn)// 本地存储一份this.alreadyRegister[type] = fn}}Person.prototype.cancle = function(type){msgCenter.cancle(type,this.alreadyRegister[type])delete this.alreadyRegister[type]}var person1 = new Person()var person2 = new Person()var person3 = new Person()var person4 = new Person()person1.register('carInfo',function(e){console.log('person1订阅了' + e.type +'的消息,内容是' +e.args.info);})person1.register('catInfo',function(e){console.log('person1订阅了' + e.type +'的消息,内容是' +e.args.info);})person2.register('carInfo',function(e){console.log('person2订阅了' + e.type +'的消息,内容是' +e.args.info);})person3.register('carInfo',function(e){console.log('person3订阅了' + e.type +'的消息,内容是' +e.args.info);})person4.register('foodInfo',function(e){console.log('person3订阅了' + e.type +'的消息,内容是' +e.args.info);})msgCenter.fire('carInfo',{info:'新型汽车上市!'})msgCenter.fire('catInfo',{info:'白色猫咪'})msgCenter.fire('foodInfo',{info:'今天有新的菜谱'})</script>

例2:

售楼处卖房子。很多的想买房子的人来咨询房子的事情,但是现在有些房子还需要等待最终的结果,打算买房子的人就把自己的个人信息都给了售楼处的人,售楼处的人等这些房子有了消息,统一打电话告诉打算房子人他们想要知道的消息,就不用每次都给来一趟售楼处来知道关于当前房子的事。

    // 售楼处var saleOffices = {}// 保存需要买房的登记的用户(订阅者)saleOffices.clientList = []// 根据订阅相同类别的房的订阅者分类保存起来saleOffices.listen = function (key, fn) {if (!this.clientList[key]) {this.clientList[key] = []}this.clientList[key].push(fn)}// 发布消息给订阅者saleOffices.trigger = function () {// [].shift.call(arguments) arguments类数组转化为数组var key = [].shift.call(arguments)fns = this.clientList[key]// 不存在要通知的订阅者if (!fns || fns.length === 0) {return false}for (var i = 0, fn; fn = fns[i++];) {fn.apply(this, arguments)}}saleOffices.listen('squareMeter88', function (price) {console.log('价格:' + price);})saleOffices.listen('1000ping', function (price) {console.log('价格:' + price);})saleOffices.trigger('squareMeter88', 20000)saleOffices.trigger('1000ping', 40000)

【策略模式】:

  • 主要用于多种状态或策略需要进行选择的时候,将所有选择都封装在一起,只给外部暴露必要的接口。
  • 使用场景:定义一族算法族,将每个算法分别封装起来,让他们可以相互替换;避免冗长的 if-else 或 switch 分支判断。
 <script>var formStrategy = (function(){var strategy = {notEmpty:function(value){return value.length ? '' : '请填写内容'},isNumber:function(value){var reg = /^[0-9]+(\.[0-9]+)?$/return reg.test(value) ? '' : '请填写一个数字'},isPhone:function(value){// 010-12345678  0033-1234567var reg = /^\d{3}-\d{8}$|^\d{4}-\d{7}$/return reg.test(value) ? '' : '请输入正确的电话号码格式'}}// 向外暴露接口return {validate:function(type,value){value = value.replace(/^\s+|\s+$/,'')return strategy[type] ? strategy[type](value) : '没有这个检测方法,请添加一个'},addStrategy:function(type,fn){if(strategy[type]){return '这个方法已经存在'}else{strategy[type] = fn}}}})()// 检测window.onload = function(){// 添加检测规则formStrategy.addStrategy('isEmail',function(value){var reg = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/return reg.test(value) ? '' : '请输入一个正确的邮箱地址'})var oInput = document.querySelector('input')oInput.onchange = function(){var resultresult = formStrategy.validate('notEmpty',this.value) || //  formStrategy.validate('isNumber',this.value) ||formStrategy.validate('isEmail',this.value)'通过检测'console.log(result);}}</script><body>请输入:<input type="text">
</body>

例2、

1.公司发奖金,根据不同等级奖金比例也是不同,为了判断每一个人应该根据等级和等级所对应的倍数得到的奖金,写出下面的代码,需要计算的部分封装成了类,Bonus 作为了Context 拥有了执行策略的能力也就是执行这些算法类。
2.定义一系列的算法,把它们各自封装成策略类,算法被封装在策略类内部的方法里。在客户对 Context发起请求的时候,Context总是把请求委托给这些策略对象中间的某一个进行计算。

<script>var strategies = {'S': function (salary) {return salary * 4},'A': function (salary) {return salary * 3},'B': function (salary) {return salary * 2}}var Bonus = function (level, salary) {return strategies[level](salary)}console.log(Bonus('S', 20000));console.log(Bonus('A', 10000));</script>

【迭代器模式】:

迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各种元素(遍历集合对象),而又不暴露该对象的内部表示。

使用场景:

1、访问一个聚合对象的内容而无须暴露它的内部表示。
2、需要为聚合对象提供多种遍历方式。 
3、为遍历不同的聚合结构提供一个统一的接口。

    // hasNext():判断迭代是否结束,返回Boolean// next():查找并返回下一个元素const arr = [1, 'red', false, 3.14]function Iterator(item) {this.item = itemthis.index = 0}Iterator.prototype = {hasNext: function () {return this.index < this.item.length},next: function () {return this.item[this.index++]}}const iterator = new Iterator(arr)while (iterator.hasNext()) {console.log(iterator.next());}// 数字区间进行迭代function Range(start, end) {return {[Symbol.iterator]: function () {return {next() {if (start < end) {return {value: start++,done: false}return {done: true,value: end}}}}}}}for (num of Range(1, 6)) {console.log(num);}</script>

【命令模式】:

  • 命令模式:将请求以命令的形式包裹在对象中,这个对象传递给调用者或理解为调用者寻找可以处理该命令的合适对象,这个对象会处理传过来命令。
  • 使用场景:命令模式的主要作用和应用场景,是用来控制命令的执行,比如,异步、延迟、排队执行命令、撤销重做命令、存储命令、给命令记录日志等;将命令的发起者和执行者解耦。

例子:比如电视它具有开和关的功能,用代码来表示的话就是一个'电视类'有两个方法'开电视,'关电视'。一般执行的时候'电视.开电视',对应上面说的'主要解决来讲'开电视这个行为和开电视这个实现都是电视来做的两者是在一起。
在定义上:这时候遥控器出现了,'开/关电视'的行为请求。可以看做两个命令,并且将这两个命令单独写成对象现在就多了两个对象'开电视' 和'关电视'他们是一组命令,多了个'遥控器'可以调用这些命令,并且把这些命令给了电视,电视依旧还是有'开电视', '关电视'的方法,不过不是直接请求调用而是遥控器间接调用了。

以去饭店吃饭为例,我们只需关心菜单上的菜,而不会必须要知道是那个厨师做的菜,因为服务员会帮我们将菜单上的'指令' 和厨师进行匹配。

<body><button id="btn1">点击按钮1</button><button id="btn2">点击按钮2</button><button id="btn3">点击按钮3</button><script>// 获取到三个按钮var btn1 = document.querySelector('#btn1')var btn2 = document.querySelector('#btn2')var btn3 = document.querySelector('#btn3')/*需求:刷新菜单目录,增加子菜单,删除子菜单receiver:服务员根据顾客命令分配厨师做菜command:顾客对菜单的操作invoker:菜单*///  真正的执行者receivedconst MenuBar = {refresh() {console.log('刷新菜单!!');}}const subMenu = {add() {console.log('增加菜单');},delete() {console.log('删除菜单');}}// 把这些方法拆分为单独的类,并且拆分后的指令需要实现统一的方法,// 方便统一调用class BaseCommand {constructor(receiver) {if (new.target === BaseCommand) {throw new Error('这个类不能被实例化')}this._receiver = receiver}// 每个指令重写这个方法execute() {}}// MenuBar方法只有一个拆分为一个指令类class refersCommand extends BaseCommand {constructor(receiver) {super(receiver)}execute() {this.receiver.refresh()}}// subMenu两个方法拆分为两个指令类class addCommand extends BaseCommand {constructor(receiver) {super(receiver)}execute() {this.receiver.add()}}class delCommand extends BaseCommand {constructor(receiver) {super(receiver)}execute() {this.receiver.delete()}}// 调用者将这些指令与receiver进行匹配function setCommand(btn, command) {btn.onclick = function () {command.execute()}}// 声明指令,将指令与原始对象进行组合const referCommand = new refersCommand(MenuBar)setCommand(btn1, referCommand)const addcommand = new addCommand(subMenu)setCommand(btn2, addcommand)</script>
</body>

【适配器模式】:

  • 适配器模式的作用是解决两个软件实体间的接口不兼容的问题。使用适配器模式之后,原本由于接口不兼容而不能工作的两个软件实体可以一起工作。
  • 使用场景:适配器模式用于补救设计上的缺陷,将不兼容的接口变得兼容;封装有缺陷的接口设计;统一多个类的接口设计;替换依赖的外部系统;兼容老版本接口;适配不同格式的数据。

百度地图和谷歌地图的渲染:

<script>var gooleMap = {show: function () {console.log('渲染谷歌地图');}}// 存在show方法时// var baiduMap = {//   show:function(){//     console.log('渲染百度地图');//   }// }var renderMap = function (map) {if (map.show instanceof Function) {map.show()}}renderMap(gooleMap) //输出:渲染谷歌地图renderMap(baiduMap) //输出:渲染百度地图// ----------------// 当百度地图没有show方法时var baiduMap = {display: function () {console.log('渲染百度地图');}}// 构造一个装饰器函数// 更换两个同一个功能的插件恰好他们的实现方法名一样var baiduMapAdapter = {show: function () {return baiduMap.display()}}renderMap(gooleMap) //输出:渲染谷歌地图renderMap(baiduMapAdapter) //输出:渲染百度地图</script>

【职责链模式】:

  • 链模式是实现链式调用的主要方法,通过在自身方法中返回自身的方式,在一个对象连续多次调用自身的方法时可以简化。
<script>var obj = {a:function(){console.log('aaa');return this},b:function(){console.log('bbb');return this}}// 链式执行obj.a().b()</script>

【模板方法模式】:

  • 模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类。通常在抽象父类中封装了子类的算法框架,包括实现一些公共方法和封装子类中所有方法的执行顺序。子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法。
  • 在模板方法模式中,子类实现中的相同部分被上移到父类中,而将不同的部分留待子类来实现。
  • 使用场景:一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现;子类中公共的行为应被提取出来并集中到一个公共父类中的避免代码重复。
 <script>// 父类--封装了子类的执行顺序abstract class Drinks {firstStep() {console.log('烧开水');}abstract secondStep();thirdStep() {console.log('倒入杯子');}abstract fourthStep();drink() {this.firstStep();this.secondStep();this.thirdStep();this.fourthStep();}}// 子类--通过继承父类,继承整个执行顺序,并重写父类的方法class Tea extends Drinks {secondStep() {console.log('泡茶叶');}fourthStep() {console.log('加柠檬');}}class Coffee extends Drinks {secondStep() {console.log('泡咖啡');}fourthStep() {console.log('加糖块');}}// 实现const tea = new Tea();tea.drink();const coffee = new Coffee();coffee.drink();
</script>

【访问者模式】:

  • 访问者模式(数据访问对象模式)主要用来抽象和封装一个对象来对数据源进行访问和存储,这样可以方便对数据的管理,以及避免数据间的重复,覆盖等问题出现。
<script>var DataVisitor = function(nameSpace,splitSign){this.nameSpace = nameSpacethis.splitSign = splitSign || '|'}DataVisitor.prototype = {status :{SUCCESS:0,FAILURE:1,OYERFLOWER:2,TIMEOUT:3},getKey:function(key){return this.nameSpace + this.splitSign + key},set:function(key,value,cbFn,expireTime){var status = this.status.SUCCESSkey = this.getKey(key)expireTime = typeof expireTime === 'number' ? expireTime + new Date().getTime() : -1try {window.localStorage.setItem(key,expireTime + this.splitSign + value)} catch (error) {status = this.status.OYERFLOWER}cbFn && cbFn.call(this,status,key,value)return value},get:function(key,cbFn){key = this.getKey(key)var status = this.status.SUCCESSvar value = window.localStorage.getItem(key)if(value){var index = value.indexOf(this.splitSign),time = value.slice(0,index)if(time > new Date().getTime() || time == -1){value = value.slice(index + this.splitSign.length)}else{value =nullstatus = this.status.TIMEOUTwindow.localStorage.removeItem(key)}}else{status = this.status.FAILURE}cbFn && cbFn.call(this,status,key,value)return value},remove:function(key,cbFn){var status = this.status.FAILUREkey = this.getKey(key)value = window.localStorage.getItem(key)if(value){value.slice(value.indexOf(this.splitSign) + this.splitSign.length)window.localStorage.removeItem(key)status = this.status.SUCCESS}cbFn && cbFn.call(this,status,key,value)}}var learnInPro = new DataVisitor('learnInPro')learnInPro.set('aaa','123',function(status,key,value){console.log(status,key,value);})learnInPro.get('aaa',function(status,key,value){console.log(status,key,value);})learnInPro.remove('aaa',function(status,key,value){console.log(status,key,value);})</script>

【等待者模式】:

  • 通过对多个异步进程的监听,对未来事件进行统一管理。
<script>function Water(){//存放异步事件var dfd = [] //存放成功的回调var doneArr = [] //存放失败的回调var failArr = []  this.when = function(){// 获取argements(是一个类数组对象)dfd = Array.prototype.splice.call(arguments)for(var i = dfd.length -1;i >=0;i--){var d = dfd[i]if(!d || d.rejected || d.resolved || !(d instanceof Defer)){dfd.splice(i,1)}}return this}// 成功之后执行的函数,相当于Promise的.thenthis.done = function(){var args = Array.prototype.slice.call(arguments)doneArr = doneArr.concat(args)return this}// 失败之后执行的函数this.fail = function(){var args = Array.prototype.slice.call(arguments)failArr = failArr.concat(args)return this}// 状态值this.Defered = function(){return new Defer()}var Defer = function(){// 状态值this.resolved = falsethis.rejected = false}Defer.prototype = {resolve:function(){this.resolved = truefor(var i=0;i<dfd.length;i++){// 全部完成,就return出去if(!dfd[i].resolved){return}}// 全部完成之后执行成功的回调函数_exec(doneArr)},reject:function(){this.rejected =true// 有未完成则执行失败的回调函数_exec(failArr)}}function _exec(arr){for(var i = 0 ;i<arr.length;i++){arr[i] && arr[i]()}}}var waiter = new Water()var async1 = function(){var dfd = waiter.Defered()setTimeout(function(){console.log('async1 done');dfd.resolve()},1000)return dfd}var async2 = function(){var dfd = waiter.Defered()setTimeout(function(){console.log('async2 done');dfd.resolve()},2000)return dfd}waiter.when(async1(),async2()).done(function(){console.log('success');}).fail(function(){console.log('fail');})</script>

【备忘录模式】:

  • 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到保存的状态。
  • 使用场景:分页控件;撤销组件。
<script>// 备忘录class Memento {constructor(content) {this.content = content;}getContent() {return this.content;}}// 备忘录列表class CareTaker {constructor() {this.list = [];}add(memento) {this.list.push(memento);}get(memento) {return this.list[index];}}// 编辑器class Editor {constructor() {this.content = null;}setContent(content) {this.content = content;}getContent() {return this.content;}saveContentMemento() {return new Memento(this.content);}getContentFromMemento(memento) {this.content = memento.getContent();}}// 实例化let editor = new Editor();let careTaker = new CareTaker();editor.setContent('测试1');editor.setContent('测试2');careTaker.add(editor.saveContentMemento());editor.setContent('测试3');
</script>

【状态模式】:

  • 状态模式允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。

  • 通常我们谈到封装,一般都会优先封装对象的行为,而不是对象的状态。但在状态模式中刚好相反,状态模式的关键是把事物的每种状态都封装成单独的类,跟此种状态有关的行为都被封装在这个类的内部,所以 button 被按下的的时候,只需要在上下文中,把这个请求委托给当前的状态对象即可,该状态对象会负责渲染它自身的行为。

有一个电灯,电灯上面只有一个开关。当电灯开着的时候,此时按下开关,电灯会切换到关闭状态;再按一次开关,电灯又将被打开。同一个开关按钮,在不同的状态下,表现出来的行为是不一样的。

<script>// 定义一个状态机var FSM = {off:{buttonWasPressed:function() {console.log('关灯');this.currentState = FSM.on;}},on:{buttonWasPressed:function() {console.log('开灯');this.currentState = FSM.off;}}}// 设置灯的初始状态var Light = function() {this.currentState = FSM.off;this.button = null;}Light.prototype.init = function() {var self = this;var button = document.createElement('button');this.button = document.body.appendChild(button);this.button.innerHTML = '开关';this.button.onclick = function() {// 把请求委托给状态机FSMself.currentState.buttonWasPressed.call(self);}}Light.prototype.setState = function (newState) {this.currentState = newState;}const light = new Light();light.init();
</script>

【中介者模式】:

  • 中介者模式的主要作用是解除对象之间的强耦合关系,通过增加一个中介者,让所有的对象通过中介者通信,而不是相互引用,所以当一个对象发生改变时,只需要通知中介者对象即可。
  • 中介模式的设计思想跟中间层很像,通过引入中介这个中间层,将一组对象之间的交互关系(依赖关系)转换成一对多(星状关系)。原本一个对象要跟n个对象交互,现在只需要跟一个中介对象交互,从而最小化对象间的交互关系,降低了代码复杂度,提高了代码的可读性和可维护性。
  • 使用场景;系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象;想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。
<script>class A {constructor() {this.number = 0;}setNumber(num, m) {this.number = num;if(m) {m.setB()}}}class B {constructor() {this.number = 0;}setNumber(num, m) {this.number = num;if(m) {m.setA();}}}// 中介者class Mediator {constructor(a,b) {this.a = a;this.b = b;}setA() {let number = this.b.number;this.a.setNumber(number*10);}setB() {let number = this.a.number;this.b.setNumber(number / 10);}}let a = new A();let b = new B();let m = new Mediator(a, b);a.setNumber(10, m);b.setNumber(10, m);
</script>

【解释器模式】:

  • 给定一个语言, 定义它的文法的一种表示,并定义一个解释器, 该解释器使用该表示来解释语言中的句子。
  • 这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式常被用在 babel、sass、less 解析、符号处理引擎等。
  • 使用场景:可以将一个需要解释执行的语言中的句子,表示为一个抽象语法树;一些重复出现的问题可以用一种简单的语言来进行表达;一个简单语法需要解释的场景。
<script>// 解释器class TerminaExpression {constructor(data) {this.data = data;}interpret(context) {if(context.includes(context)) {return true;}return false;}}// 或 组合表达式class OrExpression {constructor(expr1, expr2) {this.expr1 = expr1;this.expr2 = expr2;}interpret(context) {return this.expr1.interpret(context) || this.expr2.interpret(context);}}// 与  组合表达式class AndExpression {constructor(expr1, expr2) {this.expr1 = expr1;this.expr2 = expr2;}interpret(context) {return this.expr1.interpret(context) && this.expr2.interpret(context);}}const robert = new TerminalExpression('Robert')const john = new TerminalExpression('John')const isMale = new OrExpression(robert, john)console.log(`John is male ? ${isMale.interpret('john')}`) // John is male ? trueconst julie = new TerminalExpression('Julie')const married = new TerminalExpression('Married ')const isMarriedWoman = new AndExpression(julie, married)// Julie is a married women ? trueconsole.log(`Julie is a married women ? ${isMarriedWoman.interpret('Married Julie')}`)
</script>

WEB前端常用的设计模式总结相关推荐

  1. web前端常用网址大全

    免费接口API http://zhouxunwang.cn/ es6书籍 https://es6.ruanyifeng.com/ js菜鸟教程 https://www.runoob.com/js/js ...

  2. 14款web前端常用的富文本编辑器插件

    富文本编辑器是一种可内嵌于浏览器,所见即所得的文本编辑器.它提供类似于Office Word 的编辑功能,方便那些不太懂html用户使用,富文本编辑器的应用非常广泛,它的历史与图文网页诞生的历史几乎一 ...

  3. js统计html页面访问的次数6,JS综合篇--[总结]Web前端常用代码片段整理

    IE条件注释 条件注释简介 IE中的条件注释(Conditional comments)对IE的版本和IE非IE有优秀的区分能力,是WEB设计中常用的hack方法. 条件注释只能用于IE5以上,IE1 ...

  4. WEB前端常用JavaScript代码整理(二)

    对象的判空 将json对象转化成json字符串,然后进行判断是否等于字符串'{}',直接写{}无法进行判断 let isObject = {}; console.log(JSON.stringify( ...

  5. web前端常用网站收藏

    MDN开发者文档:https://developer.mozilla.org/zh-CN/ 菜鸟教程:https://www.runoob.com/ W3CSchool:http://www.w3sc ...

  6. web前端常用框架总结

    web框架 目前主流的 JavaScript 框架排名中,jQuery 和 Ext 可算是佼佼者,获得了用户的广泛好评.国内的一些框架很多也是仿照 jQuery 对 JavaScript 进行了包装, ...

  7. web 前端常用组件【06】Upload 控件

    因为有万恶的IE存在,所以当Web项目初始化并进入开发阶段时. 如果是项目经理,需要知道客户将会用什么浏览器来访问系统. 明确知道限定浏览器的情况下,你才能从容的让手下的封装必要的前端组件. 本篇文章 ...

  8. web前端常用知识点

    1.常见的块级元素  内联元素 div -最常用的块级元素 dl - 和dt-dd 搭配使用的块级元素 form - 交互表单 h1 -h6- 大标题 hr - 水平分隔线 ol – 有序列表 p - ...

  9. web 前端常用组件【04】Datetimepicker 和 Lodop

    1.Datetimepicker a.官方API:http://www.bootcss.com/p/bootstrap-datetimepicker/ 里面有详细的Datetimepicker介绍,包 ...

  10. 浅谈web前端常用的三大主流框架

    讲到前端的框架,大家想必都能脱口而出:Angular.React.Vue,那么这几个框架的优缺点,以及在项目当中如何抉择框架的使用等等,本篇就将介绍这三大框架的使用感受 双向绑定 开篇就用这三大框架的 ...

最新文章

  1. JS实现图片放大查看
  2. 旗帜鲜明的反对李彦宏当选院士!
  3. springboot HBase操作工具类
  4. [蓝桥杯][算法提高VIP]摆花-多重背包计数问题
  5. 台式电脑键盘字母乱了_电脑键盘上的一个不起眼的按键,特别实用却被粗心的人忽略...
  6. python rtsp转换为图片_化异为同,Python 在背后帮你做的转换
  7. acm省赛选拔组队赛经验谈
  8. 王者荣耀AI即将上线,队友再也不用担心你掉线了
  9. webp批量转换jpg_转换文件格式快人一步!右键菜单直接转换,支持图片、文档、视频等……...
  10. Mysql添加用户错误:ERROR 1364 (HY000): Field ‘ssl_cipher‘ doesn‘t have a default value解决方法
  11. 结构体做函数参数的进阶:嵌套一二级指针
  12. jszip压缩文件上传到服务器,AngularJS - 使用JSZip压缩上传的pdf文件
  13. 禁用计算机上的所有鼠标加速,win10系统电脑的鼠标加速功能怎么关闭
  14. 微信小程序制作FC模拟器,从入坑到放弃
  15. TCP/IP协议簇中三个常见的传输层协议
  16. OCR扫描识别录入之汽车车架号(VIN码)识别查询系统
  17. HIT CS:APP 计算机系统大作业 《程序人生-Hello’s P2P》
  18. 苹果官方首曝iOS 15!这些经典iPhone都要被弃
  19. 刘汝佳《算法竞赛入门经典》---总结
  20. sklearn.neighbors 最近邻相关算法,最近邻分类和回归

热门文章

  1. K3路由器官改V2.1D设置定时重启
  2. Photoshop插件-保存-存为Web所用格式-另存为-脚本开发-PS插件
  3. SAP Serial Number
  4. RTOS中动态内存和静态内存管理机制
  5. matlab求刚度,求整体刚度矩阵matlab程序.doc
  6. 东汉十三州以及各个郡的说明
  7. Windows 10 安装 Oracle 10g
  8. 中国移动微处理器CM32M101A介绍
  9. MAPGIS67默认打开方式失败
  10. ubuntu linux崩溃,ubuntu linux qq崩溃问题的解决方法以及原因