设计模式的目的
设计模式是为了更好的代码重用性, 可读性,可靠性, 可维护性
学会理解了设计模式, 就会发现生活中无处不在
设计模式分类
创建型: 工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
结构型: 适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型: 策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
以下我们将学习常见的设计模式:
四, 常见设计模式
1. 单例模式
定义: 保证一个类仅有一个实例, 并提供一个访问他的全局访问点
比如有些对象我们只需要一个, 比如线程池, 全局缓存, 登录, 购物车等, 这种我们适合使用单例模式
class SingleObj { login() {} } SingleObj.getInstance = (function() { let instance; return function() { if (!instance) { // 如果实例没有被创建, 则创建新的实例 instance = new SingleObj(); } return instance; }; })(); let a = SingleObj.getInstance(); let b = SingleObj.getInstance(); // console.log("a", a == b);复制代码
应用场景:JQuery中的$、Vuex中的Store、Redux中的Store等
2. 策略模式
定义: 定义一系列的算法, 并把他们一个个封装起来, 并且使他们可以相互替换
let strategy = { A: function(price) { return price * 2; }, B: function(price) { return price * 2; }, C: function(price) { return price * 2; } }; function calc(price, type) { return strategy[type](price); } calc(100, "A");复制代码
应用场景: 表单验证等
3. 代理模式
定义: 为一个对象提供一个代用品或占位符, 以便控制对他的访问
// 案例: jack 通过代理对象送花给rose class Flower {} class Jack { constructor(target) { this.target = target; } sendFlower(target) { // jack送花 rose收到花 const flower = new Flower(); // 创建花 this.target.receiveFlower(flower); // 目标对象收到花 } } // 目标对象 class Rose { receiveFlower(flower) { console.log("收到花", flower); } } class Proxy { constructor() { this.target = new Rose(); // 目标对象为rose } sendFlower(flower) { // 代理对象送花 this.receiveFlower(flower); } // 目标对象收到花 receiveFlower(flower) { this.target.receiveFlower(flower); } } let proxyPerson = new Proxy(); // 代理对象 let jack = new Jack(proxyPerson); // 杰克 jack.sendFlower(proxyPerson); // 杰克送花复制代码
应用场景: ES6 proxy, Vuex中对于getters访问、图片预加载等
4. 迭代器模式
定义: 按一个方法顺序访问一个聚合对象中的每一个元素, 而又无需暴露该对象的内部显示
内部迭代器
内部定义好迭代规则, 外部只需要调用一次
const each = (args, fn) => { for (let i = 0; i < args.length; i++) { const value = fn(args[i], i, args); if (!value) break; // 跳出循环 } }; each([1, 2, 3], function(item, index) { console.log(item, index); });复制代码
外部迭代器
必须显示的请求迭代下一个元素。
class Iterator { constructor(list) { this.list = list; this.index = 0; } next() { if (this.hasNext()) { return this.list[this.index++]; } return null; } hasNext() { if (this.list.length === this.index) { return false; } return true; } } let ite = new Iterator([1, 2, 3, 4]); ite.next();复制代码
应用场景: JS Iterator、JS Generator
5. 工厂模式
定义: 常见的实例化对象模式
// 具体的对象 class Product { constructor(name) { this.name = name; } } // 工厂类 class Creator { create(name) { return new Product(name); } } let creators = new Creator(); creators.create("jojo");复制代码
应用场景:JQuery中的$、Vue.component异步组件、React.createElement等
6. 发布订阅者模式
[欢迎查看本人的另外一篇文章- 发布订阅者模式解析] (juejin.cn/post/694117…)
7. 装饰者模式
定义: 在不改变对象本身的基础上, 在程序运行期间动态的添加方法
简单版
class Plane { fire() { console.log("开火"); } } class Mixin { constructor(obj) { this.obj = obj; } attack() { this.obj.fire(); console.log("开炮"); } } let plane = new Plane(); let mixin = new Mixin(plane); mixin.attack();复制代码
利用AOP给函数动态添加功能
Function.prototype.before = function(fn) { let self = this; // 保存原函数引用 return function() { fn.apply(this, arguments); // 新函数执行 return self.apply(this, arguments); // 原函数执行 }; }; Function.prototype.after = function(fn) { let self = this; return function() { let ret = self.apply(this, arguments); fn.apply(this, arguments); return ret; }; }; function f1() { console.log(1); } let f = f1 .before(function() { console.log(2); }) .after(function() { console.log(3); }); f();复制代码
应用场景:ES7装饰器、Vuex中1.0版本混入Vue时,重写init方法、Vue中数组变异方法实现等
8.适配器模式
定义: 用来解决两个接口不兼容的问题, 由一个对象包裹不兼容的对象, 比如参数转换, 允许直接访问
class Adapter { specify() { return "德国标准插头"; } } class Target { constructor(target) { this.target = new Adapter(); } requset() { let tips = this.target.specify(); console.log(`${tips} - 转换器 - 中国标准插头`); } } let target1 = new Target(); target1.requset();复制代码
9. 中介者模式
定义: 通过一个中介者对象,其他所有的相关对象都通过该中介者对象来通信,而不是相互引用,
当其中的一个对象发生改变时,只需要通知中介者对象即可。通过中介者模式可以解除对象与对象之间的紧耦合关系。
var mediator = (function() { var colorSelect = document.getElementById("colorSelect"); var memorySelect = document.getElementById("memorySelect"); var numSelect = document.getElementById("numSelect"); return { changed: function(obj) { switch (obj) { case colorSelect: //TODO break; case memorySelect: //TODO break; case numSelect: //TODO break; } } }; })(); colorSelect.onchange = function() { mediator.changed(this); }; memorySelect.onchange = function() { mediator.changed(this); }; numSelect.onchange = function() { mediator.changed(this); };复制代码
10. 外观模式
为一组复杂的子系统接口提供一个更高级的统一接口, 通过这个接口更容易访问子系统接口, 不符合开放封闭原则
class A { eat() {} } class B { eat() {} } class C { eat() { const a = new A(); const b = new B(); a.eat(); b.eat(); } } // 跨浏览器事件监听 function addEvent(el, type, fn) { if (window.addEventListener) { el.addEventListener(type, fn, false); } else if (window.attachEvent) { el.attachEvent('on' +type, fn); } else { el["on" + type] = fn; } }复制代码
应用场景:JS事件不同浏览器兼容处理、同一方法可以传入不同参数兼容处理等