肃州区住房和城乡建设局网站,网站开发系统简介,奎屯网站建设,东莞市建网站制作方案持续更新JS常用的设计模式以及应用场景*以下内容为个人简单理解以及摘抄部分网上demo组成#xff0c;有错误请在下方评论指出?*# 何谓设计模式没必要被高大上的名词所吓倒#xff0c;日常coding中或许一个不了解各种设计模式的程序员可能自己其实已经用到了很多抛开官方的定…持续更新JS常用的设计模式以及应用场景*以下内容为个人简单理解以及摘抄部分网上demo组成有错误请在下方评论指出?*# 何谓设计模式没必要被高大上的名词所吓倒日常coding中或许一个不了解各种设计模式的程序员可能自己其实已经用到了很多抛开官方的定义在我看来简单来说就是一个简单的思想被统一为规范按照这个规范可以写出更优雅可控亦或性能更佳的代码像是框架的单位软件设计模式有很多常规的有23种本文针对其中常用的几种进行简要介绍# 设计原则在列举具体的设计模式之前我们要先知道设计模式本身的规范是什么这就是设计原则主要以下三种- 单一职责原则SRP一个对象或方法只做一件事情。如果一个方法承担了过多的职责那么在需求的变迁过程中需要改写这个方法的可能性就越大。应该把对象或方法划分成较小的粒度- 最少知识原则LKP一个软件实体应当 尽可能少地与其他实体发生相互作用应当尽量减少对象之间的交互。如果两个对象之间不必彼此直接通信那么这两个对象就不要发生直接的 相互联系可以转交给第三方进行处理- 开放-封闭原则OCP软件实体类、模块、函数等应该是可以 扩展的但是不可修改当需要改变一个程序的功能或者给这个程序增加新功能的时候可以使用增加代码的方式尽量避免改动程序的源代码防止影响原系统的稳定# 从最简单的单体/单例模式开始### 定义单体一个用来划分命名空间并将一批相关的属性和方法组织在一起的对象单例顾名仅可以可以被实例化一次在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中应用该模式的一个类只有一个实例。即一个类只有一个对象实例*在java中单例的定义一个类有且仅有一个实例并且自行实例化向整个系统提供*### 优点:- 单例模式会阻止其他对象实例化其自己的单例对象的副本从而确保所有对象都访问唯一实例- 因为类控制了实例化过程所以类可以灵活更改实例化过程- 单体可以控制局部变量污染### 应用场景- 可以用单例来划分命名空间: 一些对象我们往往只需要一个如某些数据的缓存- 借助单例模式可以把代码组织的更为一致#### 最基本的单体模式直接导出一个方法属性集合的对象js// commonjs 导出module.exports {getSingleton() {return this }}#### 用闭包来实现单例jsconst Ins1 (function() { let instance null // 利用闭包特性保证实例私有化 return function(opt) { if (instance null) { instance this } for(let k in opt) { instance[k] opt[k] } return instance }
})()复制代码测试jsconst i1 new Ins1({ name: i1 })const i2 new Ins1({ name: i2 })console.log(i1 i2) // trueconsole.log(i1.name) // i2补充在node中一个文件就是一个独立模块若在某个js文件中导出一个类 class T {} export default new T 之后在其他任何外部文件多次引入其实都是保证了 T 类只被实例化了一次而不会被多次初始化。这是因为node遵循了commonjs的规范所有文件模块在被引用时都会先去模块系统的缓存中查看这个文件是否存在如果存在就返回缓存否则才会重新创建一个模块而这个缓存其实也就限制了模块内脚本的多次初始化# 策略模式### 定义就是解耦何为策略解耦 指的是定义一些列的算法把他们一个个封装起来目的就是将算法的使用与算法的实现分离开来。说白了就是以前要很多判断的写法现在把判断里面的内容抽离开来变成一个个小的个体。如大量的if else或者switch case判断当需求更改时需要添加和更改判断这违背了设计模式的对修改关闭对扩展开放的原则### 优点- 减少command c command v, 提高复用性- 遵循开闭原则算法独立易于切换、理解、拓展### 应用场景针对代码多种行为设置大量的条件判断时将每一个行为划分为多个独立的对象。每一个对象被称为一个策略。设置多个这种策略对象可以改进我们的代码质量也更好的进行单元测试#### 最简单的执行jsfunction closure() {// 定义const strategies {plus10: function(arg) {return arg 10},plus100: function(arg) {return arg 100}}// 执行return function(plus, base){return strategies[plus](base);}}const strategy closure()console.log(strategy(plus10, 1)) // 11console.log(strategy(plus100, 1)) // 101#### 对比分析eg.: 代码情景为超市促销vip为5折老客户3折普通顾客没折计算最后需要支付的金额意大利逻辑:jsfunction context (name, type, price) {if (type vip) {return price * 0.5} else if (type vip) {return price * 0.8} else {return price}}如果type类型非常多内部逻辑分别也不只是简单的return一个val那对后续的维护和测试就是灾难下面拆分逻辑为独立单元: jsclass Vip {constructor () {this.discount 0.5}getPrice (price) {return this.discount * price}}class Old {constructor () {this.discount 0.8}getPrice (price) {return this.discount * price}}class Others {constructor () {}getPrice (price) {return price}}class Context {constructor () {this.name this.strategy nullthis.price 0}setPrice (name, strategy, price) {this.name namethis.strategy strategythis.price price}getPrice () {console.log(this.name, this.strategy.getPrice(this.price), 元)return this.strategy.getPrice(this.price)}}测试jsconst seller new Contextconst vip new Vipconst old new Oldconst other new Othersseller.setPrice(zs, vip, 1000)seller.getPrice()seller.setPrice(ls, old, 1000)seller.getPrice()seller.setPrice(ww, other, 1000)seller.getPrice()// output:// zs 500 元// ls 800 元// ww 1000 元显然逻辑多而复杂时可以极大提高代码可读性以及减少维护成本# 代理模式### 定义为其他对象提供一种代理以控制对这个对象的访问。在某些情况下一个对象不适合或者不能直接引用另一个对象而代理对象可以在客户端和目标对象之间起到中介的作用著名的代理模式例子为引用计数reference counting指针对象另外代理模式还可分为- 虚拟代理把一些开销很大的对象延迟到真正需要它的时候才去创建当对象在创建前或创建中时由虚拟代理来扮演对象的替身对象创建后代理就会将请求直接委托给对象- 保护代理用于控制不同权限的对象对目标对象的访问- 缓存代理: 缓存代理可以作为一些开销大的运算结果提供暂时的存储下次运算时如果传递进来的参数跟之前一致则可以直接返回前面存储的运算结果### 优点独立职责归属便于维护测试### 应用场景比如图片的懒加载数据缓存等#### 虚拟代理实现图片懒加载jsconst imgSet (() {let node new Imagedocument.body.append(node)return function(src) {node.src src}})()const proxyImg (() {let _img new Image_img.onload function() {setTimeout(imgSet, 2000, this.src)}return function(src) {imgSet(https://yphoto.eryufm.cn/upload/assets/jump.gif)_img.src src}})()// callproxyImg(https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?imagequality100sizeb4000_4000sec1551174639di90b4757f68c9480f78c132c930c1df10srchttp://desk.fd.zol-img.com.cn/t_s960x600c5/g5/M00/02/02/ChMkJ1bKxkmIObywAArTTfACinwAALHjACDZuIACtNl408.jpg)#### 保护代理对象a需要给对象c发送信息为了保证a对c是不可见可用对象b代理转发js// filter some no use or unneed requestions or data// A --- B(proxy) ---- Cconst a {name: a,send (target, info) {target.receive(info)}}const c {name: c,receive (target, info) {console.log(c receive , info, from , target.name)}}const b {name: b,receive (info) {if (info) {c.receive(this, info)}}}a.send(b, good morning)a.send(b, )a.send(b, send again)// output:// c receive good morning from b// c receive send again from b上面表示一个最简单的保护代理#### 缓存代理顾名思义就是缓存相关的代理有一个二级别联动的标签列表第二级的各有自己所属的多个标签根据第一级的参数来发送指定请求来获取如果想要达到点击第一级列表迅速展示出相关的第二级标签我们可以在系统空闲时预先将所有标签全部获取并缓存js// 存储所有标签let tagsconst sendApiGetTags index {// ajax.get(/api, { index })}let proxyCache (async () {const allTagsCache {}const number 5const all []const params {}for (let index 0; index number; index) {all.push(sendApiGetTags({...params,index}))}const list await Promise.all(all)list.forEach((res, i) allTagsCache[i] res)return allTagsCache})()let setTags async index {// 缓存中有直接拿if (proxyCache[index]) {tags proxyCache[index]} else {// 缓存中没有则重发请求tags await sendApiGetTags(index)}}# 发布订阅模式### 定义一种一对多的依赖关系让多个订阅者对象同时监听某一个主题对象。这个主题对象在自身状态变化时会通知所有订阅者对象使它们能够自动更新自己的状态。至于发布订阅模式和观察者模式是不是同一样东西不同的人各有看法### 优点订阅者可以根据自己需求当某种Action被触发时完成自己的调度### 应用场景AngularJs的广播、vue的eventbus等#### 根据主体构建发布订阅的基类构造发布者基类jsclass Publisher {constructor () {// 订阅发布者的队列 存储每个订阅者this.subscribers []}deliver (data) {// 发布消息 调用订阅者的回调 告知订阅者this.subscribers.forEach(fn fn.shot(data))return this}}构造订阅者基类jsclass Observer {constructor (call) {// 传入订阅回调this.shot call}subscribe (publisher) {if (!publisher.subscribers.some(v v.shot this.shot)) {console.log(订阅该消息)// 判断当前订阅者是否订阅publisher.subscribers.push(this)}return this}unsubscribe (publisher) {// 移除当前订阅者console.log(取消订阅)publisher.subscribers publisher.subscribers.filter(v v.shot ! this.shot)return this}}测试jsconst pub new Publisherconst pub2 new Publisherconst obs new Observer(deliver console.log(deliver))obs.subscribe(pub) // 订阅该消息obs.subscribe(pub2) // 订阅该消息pub.deliver(pub deliver first message) // pub deliver first messagepub2.deliver(pub2 deliver first message) // pub2 deliver first messageobs.unsubscribe(pub) // 取消订阅pub.deliver(pub deliver second message) //# 装饰者模式### 定义装饰模式指的是在不必改变原类文件和使用继承的情况下动态地扩展一个对象的功能### 优点- 装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互- 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中通常是通过继承来实现对给定类的功能扩展### 应用场景- 需要扩展一个类的功能或给一个类添加附加职责- 需要动态的给一个对象添加功能这些功能可以再动态的撤销- 不必改动原本的逻辑造成不可知问题#### 给所有的函数调用添加调用前和调用后的钩子普通函数jsfunction fn(msg) {console.log(msg, right now)}fn(let go) // lets go right now我们知道JS中所有的函数都是基于父类 Function 生成的所以会继承父类原型的方法下面我们将函数的钩子挂在父类的原型上即可js// 执行前Function.prototype.before function (call) {const fn this // 返回体本身也是函数所以支持继续调用钩子return function () {// 调用钩子同时参数传递到钩子内call.apply(this, arguments)// 调用自身return fn.apply(this, arguments)}}// 执行后// 和 before 同理Function.prototype.after function (call) {const fn thisreturn function () {const res fn.apply(this, arguments)call.apply(this, arguments)// 返回自身的返回值return res}}测试js// 重新包装 fnfunction fn(msg) {console.log(msg, right now)}const decoratorFn fn.before(function (msg) {console.log(when we go,, msg)}).after(function (msg){console.log(had to go, msg)})decoratorFn(lets go)// out put:// when we go, right now// lets go, right now// had to go, right now# 职责链责任链模式### 定义它是一种链式结构每个节点都有可能两种操作要么处理该请求停止该请求操作要么把请求转发到下一个节点让下一个节点来处理请求### 优点职责链上的处理者负责处理请求客户只需要将请求发送到职责链上即可无须关心请求的处理细节和请求的传递所以职责链将请求的发送者和请求的处理者解耦了### 应用场景JS 中的事件冒泡事件委托就是经典案例#### 实例分析部门采购物品不同金额需要走不同职位的流程审批采购部经理可自主决定1w以内的采购总经理可以决定10w以内的采购董事长决定100w以内的采购下面分别抽象处理者构造基类责任链调度中心jsclass Handler {constructor() {this.next null}setNext(_handler) {this.next _handler}handleRequest(money) {}}采购部经理jsclass CGBHandler extends Handler {handleRequest(money) {// 1wif (money 10000){console.log(1w以内同意)} else {console.log(金额太大只能处理1w以内的采购)if (this.next) {this.next.handleRequest(money)}}}}总经理jsclass ZJLHandler extends Handler {handleRequest(money) {// 10wif (money 100000){console.log(10w以内同意)} else {console.log(金额太大只能处理10w以内的采购)if (this.next) {this.next.handleRequest(money)}}}}董事长jsclass DSZHandler extends Handler {handleRequest(money) {// 100wif (money 100000){console.log(10万以上的我来处理)//处理其他逻辑} }}封装客户端接口jsconst dispatch (function client() {const cgb new CGBHandler()const zjl new ZJLHandler()const dsz new DSZHandler()cgb.setNext(zjl)zjl.setNext(dsz)return cgb.handleRequest.bind(cgb)})()测试jsdispath(800000)// output:// 金额太大只能处理1w以内的采购// 金额太大只能处理10w以内的采购// 10万以上的我来处理dispath(7000)// output:// 1w以内同意补充- 纯的责任链要求请求在这些对象链中必须被处理而且一个节点处理对象要么只处理请求要么把请求转发给下个节点对象处理- 不纯的责任链要求在责任链里不一定会有处理结构而且一个节点对象即可以处理部分请求并把请求再转发下个节点处理......未完待续...?原文链接https://rollawaypoint.github.io/2019/02/24/do%20something/JS%E5%B8%B8%E7%94%A8%E7%9A%84%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/转载于:https://juejin.im/post/5c767d27518825763c6d8f96