1 前言
每个JS开发者都力求写出可维护、复用性和可读性高的代码。随着应用不断扩大,代码组织的合理性也越来越重要。设计模式为特定环境下的常见问题提供了一个组织结构,对于克服这些挑战起到至关重要的作用。
JavaScript 网页开发者在创建应用时,频繁地跟设计模式打交道(甚至在不知情的情况下)。
尽管特定环境下有各种各样的设计模式,JS 开发者还是倾向于使用一些习惯性的模式。
在这篇文章中,我将讨论这些常见的设计模式,展出优化代码库的方法,并深入解读JavaScript的内部构件。本文讨论的设计模式包括这几种:
模块设计模式
原型模式
观察者模式
单例模式
2 模式说明
设计模式
- 什么是设计模式?
=> 针对 特定问题, 给出的简洁而优化的处理方案 - 一个设计模式 A
=> 只能解决 A 类型的问题
=> 针对 B 类型的问题, 设计模式 A 解决不了 - 同一个问题, 再不同的位置, 是不一定能用同一种方案解决
- 设计模式, 只在特定的情况, 特定的时期, 针对特定的问题使用
市场上常用的设计模式
- 单例模式
- 组合模式
- 观察者模式
- 适配器模式
- 代理模式
- 工厂模式
- 构建模式
- …
单例模式
- 单: 单一, 一个, 独个
- 例: 实例(构造函数的实例化对象)
- 让一个构造函数一辈子只有一个实例对象
=> 当你需要一个构造函数一生只能 new 出一个对象的时候
=> 就可以使用单例模式
单例模式的简单应用 - 弹出层 alert() 比较丑, 用户体验极度不好
- 好多网站会使用一个自己写的 div 盒子, 当作弹出层
- 再自己写的过程中, 一个网站不可能只弹出一次
=> 不能每次弹出就创建一个 div
=> 每次弹出的都是之前创造好的那个 div, 只是文字改变了 - 创造 div 并显示出来的构造函数
=> 如果不用单例模式, 每次 new 就是创造一个 div
=> 如果用了单例模式, 每次 new 都是用的第一次的 div, 只是改变文字内容
自执行函数 - 一个正常的函数是定义完毕以后需要调用
- 自执行函数, 就是把函数当成一个表达式, 在定义完毕以后直接就调用了
- 自己的语法格式
- (function () {})()
- ~function () {}()
- !function () {}()
- 以上三种没有任何区别, 只是语法格式不一样而已
fn()()
- fn调用一次, 然后把 fn 的返回值再调用一次
- 前提: fn 的返回值必须是一个函数, 不然就报错了
(function () {})() - 自执行函数
- 前面一个括号里面写的必须是一个函数表达式, 不然就报错了
构造函数使用需要和 new 连用
- 构造函数体内如果 return 了一个基本数据类型, 写了白写
=> new 连用的时候, 得不到你 return 的基本数据类型 - 构造函数体内如果 return 了一个复杂数据类型, 构造函数白写
=> 写不写 new, 得到的都是 return 的 复杂数据类型
组合模式
- 米家:
=> 当你回家的时候, 你一打开门
=> 家里的所有东西就启动了, 灯会亮起来, 电视打开了, 空调打开了, 窗帘拉开了, … - 组合模式:
=> 把我们若干这启动方式一样的构造函数放在一起
=> 准备一个总开关, 总开关一启动, 那么这些个构造函数就都启动了
实现组合模式:
- 需要一个承载所有构造函数实例的数组
- 需要一个方法, 向数组里面添加内容
- 需要一个方法, 能把数组里面的所有内容启动了
常见应用场景
- 轮播图:
=> 基础版本的轮播图依靠定时器再左右移动
=> 一旦我切换页面以后, DOM 不动, 定时器在动, 等你再切换回来页面的时候就出问题了
=> 当我离开当前页面的时候, 应该关闭定时器
=> 等我再次回到当前页面的时候, 应该再从新启动定时器
=> 一个页面只有一个轮播图, 那么没有问题
=> 一旦一个页面多个轮播图的时候, 我们就可以整一个组合模式 - 做一个总开关
=> 离开页面的时候, 所有的轮播图都停止定时器 - 再来一个总开关
=> 回到页面的时候, 所有的轮播图再次启动
观察者模式
- 又称 发布/订阅 模式
- 目前市场上的开发人员, 分成两派人员
=> 一派人, 认为 观察者 和 发布/订阅 是一个设计模式
=> 一派人, 认为 观察者 和 发布/订阅 是两个设计模式 - 两派人到现在也没有定论
=> 目前还在互相想说服对方 - 我们的 PPT 制作的时候, 倾向于 发布/订阅 制作的
=> 个人认为是两个事情, 所以我给你们讲两个
=> 一个从 观察者 角度出发
=> 一个从 发布/订阅 角度出发
观察者角度(这两个是一个东西)
- 就像我们小时候的班主任一样
=> 班主任, 年级主任, 教务主任, 都有一个共同的能力叫做 请家长
=> 他们就是暗中观察我们的人, 一旦我们做的事情和他们想的不一样, 就回触发技能
=> 他们就是观察者, 当被观察者一旦出现变化, 立马触发他们的技能 - 被观察者
=> 就是一个学生, 你的状态就应该是 好好学习
=> 一旦你的状态改变为 玩手机
=> 立马就会通知正在观察着你的人, 观察着你的人, 就会触发技能 - 目的:
=> 让 观察者 看着 被观察者, 只要数据改变了
=> 就让 观察者 做一些事情
发布/订阅 角度
- 分成三个状态
=> 订阅
=> 取消订阅
=> 发布 - 例子: 买书
=> 我想买一本 《JavaScript 从入门到放弃》
=> 去到书店买, 如果书店没有
=> 那么我就要回家, 第二天再来看
=> 还没有, 再回家, 第三天再来看
=> 直到书店里面有了这个书, 我就买走 - 发布/订阅 的时候
=> 我想买一本 《如何治疗颈椎病》
=> 我去到书店买, 如果书店没有
=> 我就和店员说一下, 等有了你给我打电话 -> 订阅 事件
=> 我就踏实的在家等着, 等电话
=> 一旦书店到了这本书, 就会给我打电话了 -> 由店员 发布 事件
=> 再中间时间的时候, 我的朋友送了我一本这个书
=> 我就告诉店员, 你来了这个书也不需要给我打电话了 -> 取消 订阅 事件
=> 我已经由了, 不需要你在告诉我了 - js 里面由实际应用
=> addEventListener(‘click’, fn)
=> 当 click 行为触发的时候, 就会执行 fn 函数
=> 还可以绑定多个事件处理函数 (就是由好多人可以向店员订阅事件, 书到了告诉我)
=> 当 click 行为触发的时候, 所有的事件处理函数 都会执行 - 做的事情
=> 就是模仿 addEventListener 自己做一个事件监听
发布/订阅 需要的内容
- 消息盒子 {}
=> click: [fn1, fn2, fn3]
=> mouseover: [fn4, fn5, fn6] - 订阅的方法
=> 向消息盒子里面添加内容 - 取消订阅的方法
=> 把已经订阅的方法从消息盒子内部拿走 - 发布事件的方法
=> 把消息盒子里面的对应的处理函数执行了
3 总结
设计模式经常用于比较大型的应用,想知道哪种模式更具优势,来实践吧。
在构建任何应用之前,都应该全面地考虑每个角色,以及它们之间存在的关系。在回顾 模块模式、原型模式、观察者模式 和 单例模式 之后,你应该能够区分它们,并且在实际开发中使用它们了。