[TOC]
1.1. javascript进阶
1.1.1. 设计模式
设计模式概论
项目 = 模块 + 沟通
更好的组织模块
设计模块之间的沟通
提高代码质量,更加优雅
设计原则
开闭原则(对拓展开放,对源码关闭)
单一职责原则(一个模块的功能越单一越好)
依赖倒置原则(依赖抽象,而不是下层具体方法)
上层接口不会被下层接口所影响,而是在中间加入抽象层,上层去依赖抽象层
接口隔离原则(细化接口功能,使单一)
迪米特法则(两个模块之间的联系越少越好)
里氏替换原则(任何父类使用的地方,子类可以替换)
设计模式的分类
- 创建型
这些设计模式可以帮助我们优雅的创建对象
工厂模式 - 大量创建对象、单例模式 - 全局只能有我一个、建造者模式 - 精细化组合对象、原型模式 - JavaScript的灵魂
- 结构型
帮助我们优雅的设计代码结构
外观模式 - 给你一个套餐、享元模式 - 共享来减少数量、适配器模式 - 用适配代替更改、桥接模式 - 独立出来,然后再对接过去(降低耦合度)、装饰者模式 - 更优雅的扩展需求
- 行为型
模块之间行为的模式总结,帮助我们组织模块行为
观察者模式 - 我作为第三方转发、职责连模式 - 像生产线一样组织模块、状态模式 - 用状态代替判断、命令模式 - 用命令去解耦、策略模式 - 算法工厂、迭代器模式 - 告别for循环(forEach)
- 技巧型
一些帮助我们优化代码的技巧
链模式 - 链式调用、惰性模式 - 机器学习、委托模式 - 让别人代替你收快递、等待着模式 - 等你们都回来在吃饭、数据访问模式 - 一个方便的数据管理器
封装与对象
封装的目的
- 定义变量不会污染外部
- 能够作为一个模块调用
- 遵循开闭原则
好的封装满足:
- 变量外部不可见
- 调用接口来使用
- 流出扩展接口
工厂模式 - 大量创建对象
当某一个对象需要经常创建的时候(弹窗模块)
fuction Facroty(type){
switch(type){
case 'type1';
return new Type1();
case 'type2';
return new Type2()
}
}
示例:多彩的弹窗、Jquery、underscore
建造者模式 - 精细化组合一个全局对象
当需要创建单个、庞大的组合对象时(轮播图)
// 模块1
function model1(){}
// 模块2
function model2(){}
// 最终的类
function final(){
this.mode1 = new model1()
this.mode2 = new model2()
}
示例:编辑器插件,vue的初始化
单例模式 - 全局只能有我一个
为了避免重复新建,避免多个对象存在互相干扰
做法:通过定义一个方法,使用时只允许通过此方法拿到存在内部的同一个实例话对象
let Single = function(name){
this.name = name
}
Single.getInstance = function(name){
if(this.instance){
return this.instance
}
return this.instance = new Single(name)
}
示例:全局数据储存对象,vue-router
提高可复用性
可复用性的目的
- 遵循DRY原则
- 减少代码量,节省开销
特点
- 对象可以重复使用,不用修改
- 重复代码少
- 模块功能单一
桥接模式
目的:桥接代替耦合
场景:减少模块之间的耦合
三种形状,每种形状有三种颜色
颜色部分抽出来作为公用方法,形状的类都依赖于这个方法。
通过独立方法之间的桥接来形成整体功能,这样每个方法都可以被高度复用
示例:菜单上每一项都有不同的选中效果(把颜色效果抽离出来,在桥接到菜单类上)、Express中创建get等方法
```
##### 享元模式
目的:减少对象/代码数量
场景:代码中创建了大量类似对象和类似代码块
有一百种不同文字的弹窗,每种弹窗行为相同,但是文字和样式不同
保留同样的行为,提取出每个弹窗的不同部分作为外部数组
> 提取出共有部分与私有部分,私有部分作为外部数据传入,从而减少对象/代码数量
示例:多个文件上传,Jquery的extend
```javascript
function extend(){
if(arguments == 1){
for(var item in arguments[0]){
this[item] = arguments[0][item]
}
}else{
for(var item in arguments[1]){
arguments[0][item] = arguments[1][item]
}
}
}
function extend(){
var target = arguments[0]
var source
if(arguments == 1){
target = this
source = arguments[0]
}else{
target = arguments[0]
source = arguments[1]
}
for(var item in source){
target[item] = source[item]
}
}
模版方法模式
目的:定义系列操作的股价,简化后面类似操作的内容
场景:当项目中出现很多类似操作内容
先写一个基础类,特异性传入回调函数,具体实现放在回调函数。
当一个功能朝着多样化发展,不妨先定义一个基础类,把具体实现延迟到后面
示例:行为,大小,文字都会不同的一系列弹窗、封装一个算法计算器(不同地方增加不同操作)
JS组合和继承

提高代码质量
目的:
- 高质量的代码,方便后续的一切操作
- 方便他人阅读
好的代码:
- 代码整洁
- 结构规整,没有漫长的结构
- 阅读好理解
策略模式/状态模式
将方法分为几个策略放在策略对象里,直接调用对象的相应策略就可以了
在策略对象里,加入状态变量,使用时通过盖面状态来调用相应的策略
示例:动态的内容(不同用户权限不同内容)
外观模式
多个简单的接口合并成一个高级的接口
迭代器模式
在不暴露对象内部的同时,可以按顺序的访问对象内部
示例:自己的forEach
function Iteratpr(data){
this.data = data
}
Iterator.prototype.dealEach = function(fn){
if (this.data instanceof Array){
for(let i = 0; i<this.data.length;i++){
fn(this.data[i],i)
}
}else{
for(let item in this.data){
fn(this.data[item],item)
}
}
}
备忘录模式
缓存对象记录对象内部的状态,有需要时回滚到之前的状态
设计轮播图
需求描述
- 多种播放方式(渐显渐隐,左右滑动切换,上下滑动切换)
设计:
采用建造者模式来搭建模块框架
- 请求图片模块
- 检测图片加载模块(因为一般轮播图的图片比较大,而我们又需要在第一时间就展现出来,所以等待图片加载完成后再返回一个事件去渲染)
- 图片展示模块(此模块一般会重复四到六次,来展示不同的图片达到轮博的效果)
- 自动播放模块(放一个定时器,自动播放,切换图片)
- 图片的位置移动(在自动播放,或者手动切换的时候,调用此方法来实现图片的切换,在其中加入渐隐渐显的选择开关;一般采用translateX来实现移动,
- 触摸移动模块(在拖动图片的时候,会暂停自动播放模块,并根据用户的操作让图片产生暂时移动,并将上下的滑动按比例转化为水平滑动,检测滑动幅度超过阈值就调用位置移动模块,不超过则在触摸事件结束的时候弹回原位置,并开启自动播放模块)
- 轮播图模块(综合以上几个模块,可以传入图片url,是否渐显渐隐等参数,并将参数传给相应模块将其进行柯里化,方便其他模块在调用的时候,不需要在传入初始值)
商品信息缓存器
需求描述:
(1)可根据商品id判断商品是否加载过,如果加载过,直接从缓存里拿;如没有,则请求;
(2)如果是热门商品,缓存于全局的对象里;如果是非热门商品,则缓存于localStorage中;全局对象和localStorage中的商品数量上限可以配置;
(3)可主动调用api来更新某个商品的信息。