策略模式什么是代理模式?中的单例策略方法

  一、单例模式什么是单例模式实现单例模式

  <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">const singleton = function(name) {     this.name = name     this.instance = null } singleton.prototype.getName = function() {     console.log(this.name) } singleton.getInstance = function(name) {     if (!this.instance) { // 关键语句         this.instance = new singleton(name)     }     return this.instance } // test const a = singleton.getInstance('a') // 通过 getInstance 来获取实例 const b = singleton.getInstance('b') console.log(a === b) </pre>

  JavaScript 中的单例模式

  因为 JavaScript 是无类的语言,而且 JS 中的全局对象符合单例模式两个条件。很多时候我们把全局对象当成单例模式来使用

  <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">var obj = {} </pre>

  弹框层的实践

  实现弹框的一种做法是先创建好弹框,然后使之隐藏,这样子的话会浪费部分不必要的 DOM 开销,我们可以在需要弹框的时候再进行创建,同时结合单例模式实现只有一个实例,从而节省部分 DOM 开销。下列为登入框部分代码:

  <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">//弹框层的实践 const createLoginLayer = function() {     const myDiv = document.createElement('div')     myDiv.innerHTML = '登入浮框'     // myDiv.style.display = 'none'     document.body.appendChild(myDiv);     return myDiv } //使单例模式和创建弹框代码解耦 const getSingle = function(fn) {   let result = null;   return function() {       if(!result){           result = fn.apply(this, arguments);       }     return result;   } } const createSingleLoginLayer = getSingle(createLoginLayer) document.getElementById('loginBtn').onclick = function() {     createSingleLoginLayer() } </pre>

  二、策略模式什么是策略模式?JavaScript中的策略模式

  观察如下获取年终奖的 demo,根据不同的参数(level)获得不同策略方法(规则),这是策略模式在 JS 比较经典的运用之一

  <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">const strategy = {   'S': function(salary) {     return salary * 4   },   'A': function(salary) {     return salary * 3   },   'B': function(salary) {     return salary * 2   } } const calculateBonus = function(level, salary) {   return strategy[level](salary) } calculateBonus('A', 10000) // 30000 </pre>

  在函数是一等公民的 JS 中,策略模式的使用常常隐藏在高阶函数中,稍微变换下上述 demo 的形式如下,可以发现我们平时已经在使用它了,恭喜我们又掌握了一种设计模式

  <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">const S = function(salary) {   return salary * 4 } const A = function(salary) {   return salary * 3 } const B = function(salary) {   return salary * 2 } const calculateBonus = function(func, salary) {   return func(salary) } calculateBonus(A, 10000) // 30000 </pre>

  三、代理模式什么是代理模式?

  定义:为其他对象提供一种代理以控制对这个对象的访问

  主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层

  何时使用:想在访问一个对象时做一些控制

  如何解决:增加中间层

  应用实例:

  优点:1、职责清晰。2、高扩展性。3、智能化。

  缺点:1、由于在客户端和真实主体之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。2、实现代理模式需要额外的工作js权限控制,有些代理模式的实现非常复杂

  使用场景:按职责来划分,通常有以下使用场景

  远程代理

  虚拟代理

  保护(Protect or Access)代理

  Cache代理

  防火墙(Firewall)代理

  同步化(Synchronization)代理

  注意事项:

  虚拟代理实现图片预加载

  下面这段代码运用代理模式来实现图片预加载,可以看到通过代理模式巧妙地将创建图片与预加载逻辑分离,并且在未来如果不需要预加载,只要改成请求本体代替请求代理对象就行

  <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">onst myImage = (function() {   const imgNode = document.createElement('img')   document.body.appendChild(imgNode)   return {     setSrc: function(src) {       imgNode.src = src     }   } })() const proxyImage = (function() {   const img = new Image()   img.onload = function() { // http 图片加载完毕后才会执行     myImage.setSrc(this.src)   }   return {     setSrc: function(src) {       myImage.setSrc('loading.jpg') // 本地 loading 图片       img.src = src     }   } })() proxyImage.setSrc('http://loaded.jpg') </pre>

  缓存代理实现乘积计算

  <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">const mult = function() {   let a = 1   for (let i = 0, l; l = arguments[i++];) {     a = a * l   }   return a } const proxyMult = (function() {   const cache = {}   return function() {     const tag = Array.prototype.join.call(arguments, ',')     if (cache[tag]) {       return cache[tag]     }     cache[tag] = mult.apply(this, arguments)     return cache[tag]   } })() proxyMult(1, 2, 3, 4) // 24 </pre>

  Tips:在开发时候不要先去猜测是否需要使用代理模式,如果发现直接使用某个对象不方便时,再来优化不迟

  四、迭代器模式

  缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性

  使用场景:

  注意事项:迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据

  实现一个内部的迭代器

  <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">function each(arr, fn) {     for (let i = 0; i = arr.length   }   const value = function() {     return arr[current]   }   return {     next,     done,     value,   } } const arr1 = [1, 2 ,3] const arr2 = [1, 2, 3] const iterator1 = iterator(arr1) const iterator2 = iterator(arr2) const compare = function(iterator1, iterator2) {   while (!iterator1.done() && !iterator2.done()) {     if (iterator1.value() !== iterator2.value()) {       console.log('两数组不等')       return     }     iterator1.next() // 外部迭代器将遍历的权利转移到外部     iterator2.next()   }   console.log('两数组相等') } compare(iterator1, iterator2) </pre>

  五、发布订阅模式什么是发布订阅模式(观察者模式)?

  优点:

  spring权限控制_访问控制 权限控制_js权限控制

  缺点:

  使用场景:

  注意事项

  代码实现

  <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"> function pubsub() {     var _pubsub = {},//全局对象,即发布订阅对象         _topics = {}, // 回调函数存放的数组         _subUid = 0;     // 发布方法     _pubsub.publish = function (topic) {         if (!_topics[topic]) {             return false;         }         var args = [].slice.call(arguments, 1);         setTimeout(function () {             var subscribers = _topics[topic];             for (var i = 0, j = subscribers.length; i  console.log('打开音响') }) const command3 = MacroCommand() command3.add({   excute: () => console.log('打开空调') }) command3.add({   excute: () => console.log('打开电脑') }) const macroCommand = MacroCommand() macroCommand.add(command1) macroCommand.add(command2) macroCommand.add(command3) macroCommand.excute() // 煮咖啡 // 打开电视 // 打开音响 // 打开空调 // 打开电脑 </pre>

  可以看出在组合模式中基本对象和组合对象被一致对待,所以要保证基本对象(叶对象)和组合对象具有一致方法。

  Demo2-扫描文件夹

  扫描文件夹时,文件夹下面可以为另一个文件夹也可以为文件,我们希望统一对待这些文件夹和文件,这种情形适合使用组合模式

  <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">const Folder = function(folder) {   this.folder = folder   this.lists = [] } Folder.prototype.add = function(resource) {   this.lists.push(resource) } Folder.prototype.scan = function() {   console.log('开始扫描文件夹:', this.folder)   for (let i = 0, folder; folder = this.lists[i++];) {     folder.scan()   } } const File = function(file) {   this.file = file } File.prototype.add = function() {   throw Error('文件下不能添加其它文件夹或文件') } File.prototype.scan = function() {   console.log('开始扫描文件:', this.file) } const folder = new Folder('根文件夹') const folder1 = new Folder('JS') const folder2 = new Folder('life') const file1 = new File('深入React技术栈.pdf') const file2 = new File('JavaScript权威指南.pdf') const file3 = new File('小王子.pdf') folder1.add(file1) folder1.add(file2) folder2.add(file3) folder.add(folder1) folder.add(folder2) folder.scan() // 开始扫描文件夹: 根文件夹 // 开始扫描文件夹:JS // 开始扫描文件: 深入React技术栈.pdf // 开始扫描文件:JavaScript权威指南.pdf // 开始扫描文件夹:life // 开始扫描文件: 小王子.pdf </pre>

  八、装饰者模式JavaScript中的装饰者模式

  生活中的例子:天气冷了,就添加衣服来保暖;天气热了,就将外套脱下;这个例子很形象地含盖了装饰器的神韵,随着天气的冷暖变化,衣服可以动态的穿上脱下

  <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">let wear = function() {   console.log('穿上第一件衣服') } const _wear1 = wear wear = function() {   _wear1()   console.log('穿上第二件衣服') } const _wear2 = wear wear = function() {   _wear2()   console.log('穿上第三件衣服') } wear() // 穿上第一件衣服 // 穿上第二件衣服 // 穿上第三件衣服 </pre>

  这种方式有以下缺点:1:临时变量会变得越来越多;2:this 指向有时会出错

  AOP 装饰函数

  <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">// 前置代码 Function.prototype.before = function(fn) {   const self = this   return function() {     fn.apply(this, arguments)     return self.apply(this, arguments)   } } // 后置代码 Function.prototype.after = function(fn) {   const self = this   return function() {     self.apply(this, arguments)     return fn.apply(this, arguments)   } } </pre>

  用后置代码来实验下上面穿衣服的 demo

  <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">const wear1 = function() {   console.log('穿上第一件衣服') } const wear2 = function() {   console.log('穿上第二件衣服') } const wear3 = function() {   console.log('穿上第三件衣服') } const wear = wear1.after(wear2).after(wear3) wear() // 穿上第一件衣服 // 穿上第二件衣服 // 穿上第三件衣服 </pre>

  但这样子有时会污染原生函数,可以做点通变

  <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">const after = function(fn, afterFn) {   return function() {     fn.apply(this, arguments)     afterFn.apply(this, arguments)   } } const wear = after(after(wear1, wear2), wear3) wear() </pre>

  九、适配器模式什么是适配器模式?

  例如我们要把老的接口适配成新接口

  <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">// 老接口 const oldCity = (function() {     return [         {             name: 'hangzhou',             id: 11,         },         {             name: 'jinhua',             id: 12         }     ] }()) // 新接口希望是下面形式 // { //     hangzhou: 11, //     jinhua: 12 // } // 这时候就可采用适配者模式 const adaptor = function(oldCity) {     const obj = {};     for (let city of oldCity) {         obj[city.name] = city.id     }     return obj }; console.log(adaptor(oldCity)); </pre>

  end

  1. 如果觉得文章帮到您「点赞、在看、分享」一键三连,让更多人也能看到这篇文章

  2. 收藏博客地址,让我们一起成长

文章由官网发布,如若转载,请注明出处:https://www.veimoz.com/1992
0 评论
647

发表评论

!