闲话

本文写于2月初,原分享在有道云笔记上,现在转移到此。现阶段对于设计模式的理解还没有能力对本文的框架做出大的翻新,所以暂且原样搬过来了。

本文不从名目众多的设计模式谈起,而是为语言能力、代码优化、交互关系三个方面寻找解决方案,从而引出设计模式的使用和作用。覆盖了《 javascript设计模式与开发实践 》提到的全部15种javascript内常用的设计模式,具体设计模式的讲解可以翻阅此书。

一、语言特性

动态语言:程序运行,变量被赋值时才具有了类型
高阶函数:函数的参数和返回值,都可以是函数
原型继承:无严格类,通过this和原型实现赋值和继承

优点:天然面向接口,无需向上转型。创建单例无需声明类。调用函数无需创建对象。
缺点:无类别,错误容易隐藏。添加对象的初始化约束(如不按指定参数创建 throw error),繁复且不容易。

二、编程原则

设计模式的存在意义,是服务于“最佳编程实践”的思想

  1. 单一职责(SRP):一个对象(方法)只做一件事
  2. 最少知识原则(LKP 又称迪米特法则):如果两个对象之间不必彼此直接通信,那么这两个对象就不要发生直接的联系
  3. 开放-封闭原则:软件实体(类、模块、函数)等应该是可以扩展的,但是不可修改

三、场景分类

设计模式的应用场景可大致分为3类:

语言能力:语言层面的能力,能实现什么样的功能
Ⅰ:单一对象 | ①功能性用法 ② 工具性用法
Ⅱ:功能增强 | ① 平面(直接扩展) ② 切面(AOP) ③ 时间轴(多对象复用)

代码优化:抽离 “可变” 与 “不变”,提炼函数,优化代码,使维护容易
Ⅰ:提炼函数
Ⅱ:分支优化 | ① 单一因素 ② 多因素 ③ 涉及不同状态
Ⅲ:享元与模板

交互关系:简化各个对象的交互耦合模型,解耦或者降低耦合

四、解决方案

1、语言能力:(单一职责原则、开放-封闭原则)

开发中,通常遵循单一职责原则,一个对象(方法)只做一件事。
这引出了两个重要的话题:

  • Ⅰ:单一对象的功能结构
  • Ⅱ:如何对一个对象实现“功能增强”

Ⅰ:单一对象

单一对象的功能结构分两类:常规(不做讨论)、闭包

闭包:(闭包实现的常规方式都能实现。闭包的作用是保存变量的状态,但不会污染环境)

图一:功能性用法,用途体现在函数体本身。对需要记录状态的功能使用闭包(如 命令模式 的撤销undo)

图二:工具性用法,作为其他函数或对象的辅助工具。(自执行为单例,手动执行为工厂。图三为面向对象式)

① 宏命令
命令集合,一并执行。本质为 cache、add、iterator 的组合。
配合 “命令模式”,如表单验证的规则,可依次添加,打包进行

发布-订阅模式(观察者模式)
本质为 cache、add、remove、iterator 的组合,多了事件名指定 cache={“click”: [fn1,fn2…], “move”: [fn1] …… }
通常对应名字 listen、remove、trigger

组合模式
本质为 cache、add、iterator 的组合。推入堆栈的是组合对象,全部都实现 iterator 接口,执行时调用所有对象的 iterator 进行深度遍历。
叶节点需单独定义,到此终止。可增加指定 parent 的功能,实现自我栈出、删除等功能

Ⅱ:功能增强

利用特性:闭包、高阶函数

① 平面(代理、工具式包装)

图一,典型的 代理模式,以基本功能函数为框架,实现细节上的功能补充。
比如“只允许函数产生一个实例(单例模式)”、“每300毫秒只触发一次(函数节流)”、“函数支持json格式(内部转为顺序参数)”等

代理模式的理想情况:为了减少使用者的记忆负担,proxyFn 与 fn(可以是对象) 的接口和需要传入参数一致。(— fn 的 平稳升级 —)
增强版:接口的第一个参数设置为 Context 环境。(比如原函数是 fn1.fn , 可手动指定以 fn1 调用代理函数)

图二,变体, 被代理者位置从 “参数” 换到 “调用者”

图三(对象池)、图四(外部迭代器,如游标),工具式包装,针对性提供一套接口体系。
“适配器模式” 也比较类似一个简化的工具式包装

② 切面(AOP)

当逻辑的扩充,出现在原函数的切面,而非本身时
图一,“装饰者模式”,实现AOP切面编程。还可用“职责链模式”,参照下面的分支优化②,可构造 “面向切面编程” 及相关逻辑,实现 开放-封闭原则。(单一职责,不要糅合其他作用)
可以链式调用,return值加入判断,则可实现分支判断、终止链条。也可实现为 after(fn, nextFn)这种形式。

“职责链模式”是基于引用,”装饰者模式”是基于封装。因此”职责链模式”还能够实现异步AOP,而”装饰者模式”不行。

③ 时间轴(每次函数执行的数据都能被存储、索引、操作)
有序保存 fn 每次运行时产生的数据,需要利用 “堆栈”(数组或对象)

两种方式:
1. 图一,fn能返回所需数据时,只需把所有需要的数据推入堆栈(如实现 “命令模式” 的撤销N步、重演等)
2. 图二,fn只能操作数据时,需构建create方法提供API操作 fn返回的接口,并把API集合挂载在外层堆栈上(闭包,复用活动对象。活动对象被API的[[scope]]引用,不会销毁)
如:对基本的 “发布-订阅模式” 增加 namespace 寻址。由于 fn 返回的接口只能操作数据,无法把数据推入外部堆栈,必须复用活动对象(方法2)

2、代码优化:(开放-封闭原则、提炼函数)

书写代码通常的原则是:分离 “可变” 与 “不变”。
姑且分为三类:提炼函数分支优化享元与模板

Ⅰ:提炼函数

有时候函数显得肿胀不堪,导致主逻辑看不分明,此时也许需要提炼了

图一,可以分离组件,“命令模式” 封装成命令抽离出来,当做一个开关使用
某个 if 中的判断条件比较复杂,不直观时也可抽离封装,如季节月份判断:isSummer()。多次出现的代码片段也可以抽离封装起来。

Ⅱ:分支优化

当需求经常变化,对主逻辑内分支的修改可能会使代码难以维护,而且庞大的分支也影响了业务逻辑的意图表达
① “单一因素” 形成的 if-else 分支

1、 由自己指定单一变量:图一,使用 “策略模式” 抽离成若干策略,如图二。

2、 自主判断(浏览器兼容等)的分支,可用两种方式抽离:
懒加载(图三)、预加载(图四);⑵ “策略模式” + “迭代器模式”(仅当需个性化指定尝试顺序时,用 return “success” 终止)

② “多因素” 较复杂判断(if-else 或 多if)
例如:是否预约、是否预付款、是否是vip等导致优惠(结果)不同这样的复杂判断,可以考虑抽离(按结果分,结果1、2、3),使用面向切面编程串成链来

“职责链模式” 先抽离分支(如图一),构造Chain(图二)串起,通过 return “next” 向下进行,否则中断。既可以 if-else式、if式,还可以异步调用下一个。
属于面向切面编程,跟 AOP(after()、before())的不同是 AOP 是逐层封装,过多层影响性能。AOP不能异步调用下一个。

③ 对象有 “若干状态”, 执行相同操作产生不同反应(多处if-else)
例如:云产品有扫描中、正在上传、暂停中、已成功、失败等状态,每种状态下的取消、暂停等按钮反馈不一,可能充斥大量if-else,不易修改。

图一、图二是典型的“状态模式”。主逻辑是添加按钮,这属于 “不变” 。每种状态下点击事件的相应不同,这属于 “变化”
把变化抽离,每个状态一个单独的对象,让主框架来委托给状态,执行可变的逻辑。
实现的关键在于两者间能够相互引用,相互切换。活用call、apply,或者面向对象式的 this.xxx =xxx

Ⅲ:享元与模板

① “享元模式 ”
有时候,对象太多造成性能灾难,而其中性能耗费严重的属性却相同。(如试穿50件女装只需要1个女模特)

“享元模式”,分离”内部状态”、”外部状态”,每次动作之前需要先找到外部状态对象setState,可写在prototype中
注:主逻辑都在add中,创建对象使用单例代理。(一样的创建对象,一样的执行对象方法,逻辑上与处理前有很好的一致性)

② “模板方法模式”
有时候,创建了很多函数工厂,它们都要求有规定的方法、初始化规则。

“模板方法模式” 使用 init() 定义了类的基本框架,通过 “钩子” 和 throw error 使继承更稳健
上例 “状态模式” 的状态类可以用此种方式优化

3、交互关系:(最小知识原则)

多组件间的耦合关系,有时可以考虑 引入第三方,组件只需要发一个指令,由第三方来通知、改变各组件的状态
通常使用 “中介者模式” (指挥塔,是一种思想)。这非常像 “发布-订阅模式” 的特征

图一, “中介者模式”,若无中介者,每个玩家都要保存一份玩家名单、队友名单,吃药水、死亡等都要遍历名单然后调用其他玩家的接口。
中介者统一保存玩家、状态等,负责调用玩家的接口,玩家只需向其发送信息,使逻辑大大简化。(缺点:中介者可能非常庞大,逻辑复杂)
如果图一,如果增加了动态添加 operations 的方法的API,就是 “发布-订阅模式” 的模型了。

JavaScript设计模式返璞归真相关推荐

  1. 《JavaScript设计模式》——11.2 一切只因跨域

    本节书摘来自异步社区<JavaScript设计模式>一书中的第11章,第11.2节,作者:张容铭著,更多章节内容可以访问云栖社区"异步社区"公众号查看 11.2 一切只 ...

  2. 《JavaScript设计模式与开发实践》模式篇(12)—— 装饰者模式

    在传统的面向对象语言中,给对象添加功能常常使用继承的方式,但是继承的方式并不灵活, 还会带来许多问题:一方面会导致超类和子类之间存在强耦合性,当超类改变时,子类也会随之 改变;另一方面,继承这种功能复 ...

  3. JavaScript设计模式与开发实践——JavaScript的多态

    "多态"一词源于希腊文polymorphism,拆开来看是poly(复数)+ morph(形态)+ ism,从字面上我们可以理解为复数形态. 多态的实际含义是:同一操作作用于不同的 ...

  4. 16种JavaScript设计模式(中)

    简介 上文中介绍了学习设计模式前需要了解的一些基础概念和js的基础模式-原型模式,没看过的同学可以点这里,本章将介绍以下几种模式 单例模式 策略模式 代理模式 迭代器模式 发布订阅模式 命令模式 组合 ...

  5. javascript 设计模式(一)

    1.为什么要深入学习Javascript? 用户对页面的美观性,易用性要求越来越高 Javascript+HTMl5+CSS3将是未来客户端技术的潮流 学好JS有助于更好学习JS库.JS框架 开发自己 ...

  6. JavaScript设计模式--简单工厂模式例子---XHR工厂

    JavaScript设计模式--简单工厂模式例子---XHR工厂 第一步,Ajax操作接口(目的是起一个接口检测作用) (1)引入接口文件 //定义一个静态方法来实现接口与实现类的直接检验 //静态方 ...

  7. JavaScript 设计模式基础(二)

    JavaScript 设计模式基础(一) 原型模式 在以类为中心的面向对象编程语言中,类和对象的关系就像铸模和铸件的关系,对象总是从类中创建.而原型编程中,类不是必须的,对象未必从类中创建而来,可以拷 ...

  8. 《JavaScript设计模式与开发实践》阅读摘要

    <JavaScript设计模式与开发实践>作者:曾探 系统的介绍了各种模式,以及js中的实现.应用,以及超大量高质量代码,绝对值得一读 面向对象的js 静态类型:编译时便已确定变量的类型 ...

  9. JS代理模式《JavaScript设计模式与开发实践》阅读笔记

    代理模式 代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问. 保护代理和虚拟代理 保护代理:当有许多需求要向某对象发出一些请求时,可以设置保护代理,通过一些条件判断对请求进行过滤. 虚拟 ...

最新文章

  1. WPF 使用DrawingVisual绘制高性能曲线图
  2. 揭秘阿里秒级百万TPS平台架构实现
  3. 人类遗传变异神库 | ClinVar数据库详解
  4. 中科院遗传所钱文峰组发表新冠病毒源于自然界的科学证据
  5. php实现数值的整数次方
  6. android图片异步加载解决步骤
  7. 《天天数学》连载38:二月七日
  8. v380怎么设置云存储_计算机网络云计算技术在应用中的不足及缺陷
  9. 户口所在地代码查询_毕业生如何查询档案存放地及存档问题?
  10. CSDN 日报第 1 期:蚂蚁集团将向机构投资者退款;Pyston v2 发布 比 Python 快 20%
  11. [日志]08/19/2007
  12. tomcat7源代码Bootstrap
  13. java手机翻译,使用JUniversal翻译Android项目
  14. threejs写的模仿微信跳一跳游戏
  15. MySQL-查询本周过生日的人-终极答案
  16. Kubernetes 学习笔记(一)--- 基本概念及利用kubeadm部署K8S
  17. SAP 打开总账科目
  18. IT项目经理沟通技巧的重要性
  19. 系统安全启动总结思考
  20. program received signal sigsegv,Segmentation fault的解释(可能并不全面)

热门文章

  1. 第一届LCI workshop @ ICCV 2019, 欢迎投稿
  2. 新年快乐!我爱计算机视觉干货集锦与新年展望
  3. 谷歌发布AdaNet,快速灵活的AutoML工具,帮助开发者构筑强大集成学习模型
  4. PyTorch | torch.from_numpy使用方法 | torch.from_numpy如何使用?torch.from_numpy()例子 | 通过torch.from_numpy创建张量
  5. 一切为了开放科学!Papers with Code 新增CS、物理、数学、统计学等多个学科
  6. 经验 | 计算机视觉顶会上的灌水文都有哪些特征?
  7. 【资源下载】512页IBM沃森研究员Charu最新2018著作《神经网络与深度学习》(附下载链接)
  8. 如何在电脑上制作请假条表格_条码标签打印软件如何制作请假表
  9. sql语句中使用函数会耗费性能吗_挽救数据库性能的 30 条黄金法则 | 原力计划...
  10. Java正则获取a标签href_正则表达式,同时获取a标签里的href,text 的值