[深入23] JS设计模式 - 代理,策略,单例

导航

[react] Hooks

[React 从零实践01-后台] 代码分割
[React 从零实践02-后台] 权限控制
[React 从零实践03-后台] 自定义hooks
[React 从零实践04-后台] docker-compose 部署react+egg+nginx+mysql
[React 从零实践05-后台] Gitlab-CI使用Docker自动化部署

[源码-webpack01-前置知识] AST抽象语法树
[源码-webpack02-前置知识] Tapable
[源码-webpack03] 手写webpack - compiler简单编译流程
[源码] Redux React-Redux01
[源码] axios
[源码] vuex
[源码-vue01] data响应式 和 初始化渲染
[源码-vue02] computed 响应式 - 初始化,访问,更新过程
[源码-vue03] watch 侦听属性 - 初始化和更新
[源码-vue04] Vue.set 和 vm.$set
[源码-vue05] Vue.extend

[源码-vue06] Vue.nextTick 和 vm.$nextTick
[部署01] Nginx
[部署02] Docker 部署vue项目
[部署03] gitlab-CI

[数据结构和算法01] 二分查找和排序

[深入01] 执行上下文
[深入02] 原型链
[深入03] 继承
[深入04] 事件循环
[深入05] 柯里化 偏函数 函数记忆
[深入06] 隐式转换 和 运算符
[深入07] 浏览器缓存机制(http缓存机制)
[深入08] 前端安全
[深入09] 深浅拷贝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模块化
[深入13] 观察者模式 发布订阅模式 双向数据绑定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手写Promise
[深入20] 手写函数
[深入21] 数据结构和算法 - 二分查找和排序
[深入22] js和v8垃圾回收机制
[深入23] JS设计模式 - 代理,策略,单例

(一) 前置知识

  • 设计模式一直都没敢写,主要是个人觉得理解不难,主要胆怯在运用于生产项目

(1) 一些单词

pattern: 模式 // design pattern: 设计模式
strategy: 策略,战略

performance: 性能,绩效
salary: 工资,薪水
bonus: 奖金

singleton: 单身,单例模式

(2) Storage

  • 概念
    • 两个对象部署了 ( Storage接口 ),分别是 ( window.localStorage ) 和 ( window.sesstionStorage )
  • 方法
    • Storage.setItem(key, value)
      • key 必须是符串,非字符串会被转成字符串
      • value 也必须是字符串,非字符串会被转成字符串
      • 如果key已经存在,则新的value覆盖旧的value
      • 如果存储空间已满,该方法会抛错
      • Storage.setItem('name', 'woow_wu7') 等价于 Storage.name = 'woow_wu7'
    • Storage.getItem(key)
      • key不存在,则返回 null
    • Storage.removeItem(key)
      • 清除key对应的value
    • Storage.clear()
      • 清除Storage中的所有数据
      • 返回值是undefined
    • Storage.key(number)
      • 接受一个整数作为参数,返回该位置对应的键值
  • 事件
    • window.addEventListener('storage', onStorageChange)
    • 该监听函数的event对象
      • StorageEvent.key: 发生变动的键名
      • StorageEvent.newValue: 新的键值,字符串
      • Storage.oleValue: 旧的键值,字符串
      • Storage.storageArea: 返回整个对象
      • Storage.url: 表示原始触发 storage 事件的 ( 网页地址 )
  • 特别注意
    • 该事件不在导致数据变化的当前页面触发,而是在 ( 同一个域名 ) 的其他窗口触发
    • 所以:如果浏览器只有一个窗口,可能看不到该事件触发
    • 所以:可以实现多窗口通信
      image

(3) 对象的命令空间

var namespace = {
  a: () => { console.log('a') },
  b: () => { console.log('b') },
}

(二) JS设计模式

(1) 代理模式

(1) 代理模式的定义

  • 代理模式: 指给某一个对象提供一个 ( 代理对象 ),并由 ( 代理对象 ) 来控制 ( 原对象的引用 ),代理模式类似 ( 中介 )

(2) 为什么要使用代理模式?

  • 中介隔离
    • ( 客户对象 ) === ( 代理对象 ) === ( 委托对象 )
    • 一些情况下,客户对象不想或者不能直接引用一个委托对象,则代理对象可以充当中介作用
  • 开闭原则,增加功能
    • ( 代理类 ) 除了是 ( 客户类 ) 和 ( 委托类 ) 的中介之外,还可以给代理类增加额外的功能来扩展委托类的功能,这样我们就可以直接修改代理类而不是直接修改委托类,符合代码设计的开闭原则
    • ( 代理类 ) 主要负责委托类的 ( 预处理消息, 过滤消息, 转发消息给委托类,对返回结果的处理 )
    • ( 代理类 ) 本身不提供服务,而是通过调用委托类相关的方法,来提供特定的服务,真正的业务功能还是由委托类来实现

(3) 实战

(3-1) 代理ajax请求 - 增加缓存功能

  • 作用
    • 通过 代理函数proxyRequest() 调用 cache() 使得 request() 方法做了一层缓存
    • 缓存每次请求的参数,如果参数一样,就直接返回之前参数对应的 Map 中的缓存的 value
  • 原理
    • 利用 ( 代理模式 ) 给原由的函数添加新的逻辑但又不影响原函数,相当于用 ( 函数组合 ) 来实现 ( 复用逻辑和添加逻辑 )



  
  
  Document


  


(3-2) 缓存代理 - 处理缓存过期时间 - class版本




  
  
  Document


  


image

2021/05/03 优化

(3-2) 优化缓存代理 - 处理缓存过期时间 - class版



  
    
    
    
    Document
  
  
    
  


(3-3) 缓存代理 - 处理缓存过期时间 - function版本




  
  
  Document


  


image

(2) 策略模式

(1) 概念

  • 定义一系列算法,把它们一个个封装起来,并使它们可以 ( 相互替换 )
  • 将 ( 不变的部分 ) 和 ( 变化的部分 ) 隔开
  • ( 策略模式 ) 的主要目的就是将 ( 算法的使用 ) 和 ( 算法的实现 ) 分离开来

(2) 组成 - 策略模式组要包含 ( 策略类 ) 和 ( 环境类 )

  • 策略类
    • 封装了具体的算法,并负责具体的计算过程
  • 环境类 context
    • 环境类context接受客户的请求,随后把请求 ( 委托 ) 给某一个策略类
    • 环境类context中要维持对某个策略类的引用

(3) 特点

  • 避免多重选择语句出现:策略模式利用 ( 组合,委托,多态 ) 等技术和思想,可以有效的避免 ( 多重条件选择语句if...else ) 的情况
  • 符合开放封闭原则:将算法封装在独立strategy策略中,使得它们容易切换,理解,扩展
  • 算法可复用:可以复用在系统其他地方,从而避免冗余重复的工作

(4) 缺点

  • 必须了解所有策略:必须了解各个策略的不同点,才能实现一个特定的策略

(5) 实战

(5-1) 计算奖金

  • S绩效 - 4倍工资
  • A绩效 - 3倍工资
  • B绩效 - 2倍工资
  • bonus: 奖金
  • salary: 工资
  • performance: 绩效,性能
  • strategy: 策略
  • strategy pattern 策略模式
(1) 未经过任何优化的写法
- 缺点
  - 具有很多if...else语句
  - 缺乏扩展性:如要添加C绩效,则需要修改内部的函数实现,违反开放封闭原则
  - 复用性差
function getBonus(performance, salary) { // bonus奖金 performance绩效 salary奖金
  if ( performance === 'S') return 4 * salary;
  if ( performance === 'A') return 3 * salary;
  if ( performance === 'B') return 2 * salary;
}
getBonus('S', 1000) // 输出:4000



---------------
(2) 使用 - 函数组合 - 重构代码
function getS (salary) { return 4 * salary }
function getA (salary) { return 3 * salary }
function getB (salary) { return 2 * salary }
function getBonus(performance, salary) {
  if ( performance === 'S') return getS(salary)
  if ( performance === 'A') return getA(salary)
  if ( performance === 'B') return getB(salary)
}
getBonus('S', 1000) // 输出:4000



---------------
(3) 使用 - 策略模式strategyPattern - 重构代码
- 优点
  - 消除了大量if...else语句
  - 所有计算奖金bonus的逻辑都不在getBonus函数即环境类context中,而是分布在各个策略对象strategy中
  - context环境类不负责计算,只是负责将请求委托给strategy策略类
const strategy = {
  S: (salary) => 4 * salary,
  A: (salary) => 3 * salary,
  B: (salary) => 2 * salary,
}
const getBonus = (performance, salary) => strategy[performance](salary)
getBonus('S', 1000) // 输出:4000

(5-2) 表单验证

  • 未优化的代码如下
(1) 未优化的代码如下




  
  
  Document


  
  • 使用策略模式 - 优化表达验证
    • 第一步:将验证逻辑封装到 ( 策略对象strategy中 )
    • 第二步:将用户的请求通过 ( 环境对象context ) 委托给策略对象strategy来处理
(2) 使用策略模式 - 优化表达验证




  
  
  Document


  
image

(3) 单例模式 singleton

(1) 定义

  • 保证一个类只有一个实例,并提供一个访问它的全局访问点
  • 优点
    • ( 创建对象 ) 和 ( 管理单例 ) 被分布在不同的方法中

(2) 应用场景

  • 全局变量
  • 连接数据库,防止多次连接或断开
  • 全局状态管理redux vuex
  • 登陆框,form表单,loading层等

(3) 实现

(3-1) 闭包实现一个单例

  • 缺点:需要调用 getInstance 函数创建对象
function Singleton(name) { // 构造函数, singleton: 单身, 注意:箭头函数不能作为构造函数,arguments,yeild命令
  this.name = name
}
Singleton.getInstance = (() => {
  let instance = null // 闭包变量
  return (name) => {
    if (!instance) {
      instance = new Singleton(name)
    }
    return instance
  }
})(); // IIFE立即调用的函数表达式,注意小括号和中括号开头的前一条语句需要加分号,或者小括号中括号前加分号

const a = Singleton.getInstance('A')
const b = Singleton.getInstance('B') // 这一次传参没用了
console.log('a === b', a === b) // true

(3-2) 函数实现一个单例

  • 缺点:需要调用 getInstance 函数创建对象
function Singleton(name) {
  this.name = name
}
Singleton.getInstance = function(name) {
  // 注意:
  //  1. 这里的 this 指向的是 Singleton ,而不是 Singleton生成的实例
  if (!this.instance) {
    this.instance = new Singleton(name)
  }
  return this.instance
}
const a = Singleton.getInstance('A')
const b = Singleton.getInstance('B')
console.log('a === b', a === b)
console.log('a', a)
console.log('b', b)
console.log('Singleton.instance', Singleton.instance) // instance直接是Singleton的属性

(3-3) 透明的单例模式

  • 优点:解决12中的需要调用 createInstance 函数来生成对象的缺点,这里直接通过new调用
  • 缺点:不符合单一职责原则,这个对象其实负责了两个功能:单例和创建对象
var Singleton = (function() {
  var instance = null
  return function(name) {
    if (instance) return instance
    this.name = name
    return instance = this // 返回实例对象,instance仅仅是为了做判断
  }
})(); // IIFE返回一个构造函数
const a = new Singleton('A')
const b = new Singleton('B')
console.log('a === b', a === b)
console.log('a', a)

(3-4) es6实现单例

class Book {
  constructor(name) {
    if (!Book.intance) {
      this.name = name
      Book.intance = this
    }
    return Book.intance
  }
}
const english = new Book('english')
const chinese = new Book('chinese')
console.log('english === english', english === english)
console.log('english', english)
console.log('chinese', chinese)

(4) 实战 - 登陆框




  
  
  Document


  
  



image

资料

  • 代理模式 https://juejin.cn/post/6844904190947360781
  • 策略模式1 https://juejin.cn/post/6844903751225081864
  • 策略模式2 https://juejin.cn/post/6844903753317875720#heading-0
  • 单例模式A https://juejin.cn/post/6844903751204110344#heading-0
  • 单例模式B https://juejin.cn/post/6894516731256487949

你可能感兴趣的:([深入23] JS设计模式 - 代理,策略,单例)