导航
[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)
- 接受一个整数作为参数,返回该位置对应的键值
- Storage.setItem(key, value)
- 事件
window.addEventListener('storage', onStorageChange)
- 该监听函数的event对象
- StorageEvent.key: 发生变动的键名
- StorageEvent.newValue: 新的键值,字符串
- Storage.oleValue: 旧的键值,字符串
- Storage.storageArea: 返回整个对象
- Storage.url: 表示原始触发 storage 事件的 ( 网页地址 )
- 特别注意
该事件不在导致数据变化的当前页面触发,而是在 ( 同一个域名 ) 的其他窗口触发
- 所以:如果浏览器只有一个窗口,可能看不到该事件触发
- 所以:可以实现多窗口通信
(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
2021/05/03 优化
(3-2) 优化缓存代理 - 处理缓存过期时间 - class版
Document
(3-3) 缓存代理 - 处理缓存过期时间 - function版本
Document
(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
(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
资料
- 代理模式 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