# 什么是设计模式
设计模式是解决特定问题的经验总结和可复用的解决方案。可以提高代码的复用性、可维护性和可读性,是提高开发效率的重要手段。它有基本的五大原则可见 SOLID 原则在前端中的实践。
下面以 JS 的角度介绍一些在前端中常用的设计模式。
# 单例模式
全局只允许创建一个实例的模式,在前端中常用于:
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class Singleton { constructor() { if (!Singleton.instance) { Singleton.instance = this; } return Singleton.instance; } }
const a = new Singleton(); const b = new Singleton();
console.log(a === b);
|
# 工厂模式
根据不同的参数创建不同对象的模式,通过工厂类来创建对象,将创建和使用分离。
例如我们想通过 JSON 数组来生成不同的表单,未使用工厂模式:
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
| class TextInput { constructor(name) { this.name = name; } render() { return `<div class="form-text"><input type="text" name="${this.name}""></div>`; } }
class CheckboxInput { constructor(name) { this.name = name; } render() { return `<div class="form-checkbox"><input type="checkbox" name="${this.name}"></div>`; } }
function renderForm(formConfig) { return formConfig.map(field => { switch (field.type) { case 'text': return new TextInput(field.name).render(); case 'checkbox': return new CheckboxInput(field.name).render(); } }).join(''); }
const formConfig = [ { type: 'text', name: 'username' }, { type: 'checkbox', name: 'agree' }, ];
const formHTML = renderForm(formConfig);
|
上面的 renderForm 函数即负责了业务判断逻辑又负责了生成渲染,这违背了单一职责原则。
我们可以借助一个工厂类来负责判断逻辑,使外部函数只负责 UI 渲染。
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
| class TextInput { } class CheckboxInput { }
class FormInputFactory { createInput(fieldConfig) { switch (fieldConfig.type) { case 'text': return new TextInput(fieldConfig.name); case 'checkbox': return new CheckboxInput(fieldConfig.name); } } }
function renderFormWithFactory(formConfig) { const factory = new FormInputFactory(); return formConfig.map(field => factory.createInput(field).render()).join(''); }
const formConfig = [ ];
const formHTMLWithFactory = renderFormWithFactory(formConfig);
|
# 观察者模式
一种对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会自动更新。在前端中常用于:
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
| class Subject { constructor() { this.observers = []; }
addObserver(observer) { this.observers.push(observer); }
removeObserver(observer) { const index = this.observers.indexOf(observer); if (index !== -1) { this.observers.splice(index, 1); } }
notify(data) { this.observers.forEach(observer => observer.update(data)); } }
class Observer { constructor(name) { this.name = name; }
update(data) { console.log(`${this.name}收到了${data}`); } }
const observer1 = new Observer('imtx'); const observer2 = new Observer('bubu');
const subject = new Subject(); subject.addObserver(observer1); subject.addObserver(observer2);
subject.notify('hello world!');
|
# 装饰器模式
一种在不改变对象自身的基础上,动态地给对象增加新的功能的模式。在前端开发中,常用于实现组件的复用和功能的增强等。符合开放 / 封闭原则。
下面的代码通过装饰器不断新增标签特性,而非直接创建若干个子类。
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
| class TextComponent { constructor(text) { this.text = text; } render() { return this.text; } }
class TextDecorator { constructor(component) { this.component = component; } render() { return this.component.render(); } }
class BoldTextDecorator extends TextDecorator { render() { return `<strong>${super.render()}</strong>`; } }
class ItalicTextDecorator extends TextDecorator { render() { return `<em>${super.render()}</em>`; } }
const simpleText = new TextComponent("Hello"); console.log("Simple Text:", simpleText.render());
const boldText = new BoldTextDecorator(simpleText); console.log("Bold Text:", boldText.render());
const italicBoldText = new ItalicTextDecorator(boldText); console.log("Italic Bold Text:", italicBoldText.render());
const italicSimpleText = new ItalicTextDecorator(simpleText); console.log("Italic Text:", italicSimpleText.render());
const boldItalicText = new BoldTextDecorator(italicSimpleText); console.log("Bold Italic Text:", boldItalicText.render());
|
# 代理模式
通过一个代理对象控制对目标对象的访问的模式。在前端常用于:
在 ES6 提供了 Proxy 对象来实现代理模式。
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 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
| const handlerApiSummary = {
get: function(target, property, receiver) { },
set: function(target, property, value, receiver) { },
has: function(target, property) { },
deleteProperty: function(target, property) { },
apply: function(target, thisArg, argumentsList) { },
construct: function(target, argumentsList, newTarget) { },
getOwnPropertyDescriptor: function(target, property) { },
defineProperty: function(target, property, descriptor) { },
getPrototypeOf: function(target) { },
setPrototypeOf: function(target, prototype) { },
ownKeys: function(target) { },
preventExtensions: function(target) { },
isExtensible: function(target) { } };
const targetObject = {}; const proxyExample = new Proxy(targetObject, handlerApiSummary);
console.log("Proxy API Summary (No Actual Implementation):", proxyExample);
|
参考文章:
稀土掘金 - 前端必须掌握的 7 种设计模式