设计模式是从许多优秀的软件系统中,总结出的成功的、能够实现可维护性、复用的设计方案,使用这些方案将可以让我们避免做一些重复性的工作。
单例模式(弹框)
一个类只能构造出唯一实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Single { constructor(name) { this.name = name; } static getInstance(name) { if (!this.instance) { this.instance = new Single(name); } return this.instance; } }
let single1 = Single.getInstance("name1"); let single2 = Single.getInstance("name2"); console.log(single1 === single2);
|
策略模式(表单验证)
根据不同参数命中不同的策略
策略模式的表单验证示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| const strategies = { isNoEmpty: function(value, errorMsg) { if (value.trim() === "") { return errorMsg; } }, minLength: function(value, length, errorMsg) { if (value.trim().length < length) { return errorMsg; } }, maxLength: function(value, length, errorMsg) { if (value.length > length) { return errorMsg; } }, isMobile: function(value, errorMsg) { if ( !/^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|17[7]|18[0|1|2|3|5|6|7|8|9])\d{8}$/.test( value ) ) { return errorMsg; } } };
class Validator { constructor() { this.cache = []; this.errList = []; } add(value, rules) { for (let i = 0, rule; (rule = rules[i++]); ) { let strategyAry = rule.strategy.split(":"); let errorMsg = rule.errorMsg; this.cache.push(() => { let strategy = strategyAry.shift(); strategyAry.unshift(value); strategyAry.push(errorMsg); let error = strategies[strategy](...strategyAry "strategy"); if (error) { this.errList.push(error); } }); } } start() { for (let i = 0, validatorFunc; (validatorFunc = this.cache[i++]); ) { validatorFunc(); } return this.errList; } }
let validataFunc = function(info) { let validator = new Validator(); validator.add(info.userName, [ { strategy: "isNoEmpty", errorMsg: "用户名不可为空" }, { strategy: "minLength:2", errorMsg: "用户名长度不能小于2位" } ]); validator.add(info.password, [ { strategy: "minLength:6", errorMsg: "密码长度不能小于6位" } ]); validator.add(info.phoneNumber, [ { strategy: "isMobile", errorMsg: "请输入正确的手机号码格式" } ]); return validator.start(); };
let userInfo = { userName: "王", password: "1234", phoneNumber: "666" }; let errorMsg = validataFunc(userInfo); console.log(errorMsg);
|
代理模式(图片预加载)
代理对象和本体对象具有一致的接口
图片代理模式示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| let relImage = (function () { let imgNode = document.createElement("img"); document.body.appendChild(imgNode); return { setSrc(src) { imgNode.src = src; }, }; })(); let proxyImage = (function () { let img = new Image(); img.onload = function () { relImage.setSrc(img.src); }; return { setSrc(src) { img.src = src; relImage.setSrc( "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg" ); }, }; })();
proxyImage.setSrc( "https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg" );
|
装饰者模式(在函数执行前后添加新的方法)
在不改变对象自身的基础上,动态地给某个对象添加一些额外的职责
装饰者模式示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| function fuc() { console.log(2); } Function.prototype.before = function (beFn) { let self = this; return function () { beFn.apply(this, arguments); return self.apply(this, arguments); }; }; Function.prototype.after = function (afFn) { let self = this; return function () { self.apply(this, arguments); return afFn.apply(this, arguments); }; };
function fuc1() { console.log(1); } function fuc3() { console.log(3); } function fuc4() { console.log(4); }
fuc = fuc.before(fuc1).before(fuc4).after(fuc3); fuc();
|
组合模式(打印文件目录)
组合模式在对象间形成树形结构
组合模式中基本对象和组合对象被一致对待
无须关心对象有多少层, 调用时只需在根部进行调用
函数组合模式示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| class Combine { constructor() { this.list = []; } add(fn) { this.list.push(fn); return this; } excute() { for (let i = 0; i < this.list.length; i++) { this.list[i].excute(); } } } let comb1 = new Combine(); comb1 .add({ excute() { console.log(1); }, }) .add({ excute() { console.log(2); }, });
let comb2 = new Combine(); comb2 .add({ excute() { console.log(3); }, }) .add({ excute() { console.log(4); }, });
let comb3 = new Combine(); comb3 .add({ excute() { console.log(5); }, }) .add({ excute() { console.log(6); }, }); comb2.add(comb3);
let comb4 = new Combine(); comb4.add(comb1).add(comb2); comb4.excute();
|
工厂模式(jquery 中的 window.$)
工厂模式是用来创建对象的一种最常用的设计模式
不暴露创建对象的具体逻辑,而是将逻辑封装在一个函数中,这个函数就可以被视为一个工厂
工厂模式示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Car { constructor(name, color) { this.name = name; this.color = color; } } class Factory { static create(type) { switch (type) { case "car": return new Car("汽车", "白色"); break; case "bicycle": return new Car("自行车", "黑色"); break; default: console.log("没有该类型"); } } } let p1 = Factory.create("car"); let p2 = Factory.create("bicycle"); console.log(p1, p1 instanceof Car); console.log(p2, p2 instanceof Car);
|
访问者模式(babel 插件)
在不改变该对象的前提下访问其结构中元素的新方法
访问者模式示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| class Student { constructor(name, chinese, math, english) { this.name = name; this.chinese = chinese; this.math = math; this.english = english; }
accept(visitor) { visitor.visit(this); } }
class ChineseTeacher { visit(student) { console.log(`语文 ${student.chinese}`); } }
class MathTeacher { visit(student) { console.log(`数学 ${student.math}`); } }
class EnglishTeacher { visit(student) { console.log(`英语 ${student.english}`); } }
const student = new Student("张三", 90, 80, 60);
const chineseTeacher = new ChineseTeacher(); const mathTeacher = new MathTeacher(); const englishTeacher = new EnglishTeacher();
student.accept(chineseTeacher); student.accept(mathTeacher); student.accept(englishTeacher);
|
发布订阅模式(EventBus)
订阅者订阅相关主题,发布者通过发布主题事件的方式,通知订阅该主题的对象
手写发布订阅模式示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| class EventBus { constructor() { this.task = {}; } on(type, fn) { if (!this.task[type]) this.task[type] = []; this.task[type].push(fn); } emit(type, ...args) { if (this.task[type]) { this.task[type].forEach((fn) => { fn.apply(this, args); }); } } off(type, fn) { if (this.task[type]) { this.task[type] = this.task[type].filter((item) => item !== fn); } } once(type, fn) { function f(...args) { fn(...args); this.off(type, f); } this.on(type, f); } }
let event = new EventBus(); event.on("change", (...args) => { console.log(args); });
event.once("change", (...args) => { console.log(args); }); event.emit("change", 1, 2); event.emit("change", 2, 3);
|
观察者模式(vue 双向绑定)
一个对象有一系列依赖于它的观察者(watcher),当对象发生变化时,会通知观察者进行更新
观察者模式示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| let data = { name: "ming", age: 18, }; Object.keys(data).forEach((key) => { let value = data[key]; Object.defineProperty(data, key, { get() { console.log("get", value); return value; }, set(newValue) { console.log("更新"); value = newValue; }, }); }); data.name = "佩奇"; console.log(data.name);
|
观察者与发布订阅模式的区别
观察者模式:一个对象有一系列依赖于它的观察者(watcher),当对象发生变化时,会通知观察者进行更新
发布订阅模式:订阅者订阅相关主题,发布者通过发布主题事件的方式通知订阅该主题的对象,发布订阅模式中可以基于不同的主题去执行不同的自定义事件