最近终于看完买了很久的《Javascript设计模式与开发实践》,感觉还是受益匪浅的。 在这里做了一个简单的小结,将书上提到的设计模式尽量用自己的思路写出来,当然也是有所借鉴。书上的代码都是基于ES5的,本次小结会用到ES6来写。 感觉应该还是蛮多错漏的,欢迎指正。
单例模式(Singleton pattern)
确保一个类只有一个实例,并提供对该实例的全局访问。
function createSingleton(construct) { let storage = null; let handle = { construct: function(trapTarget, argumentList) { if (!storage) { storage = new trapTarget(argumentList[0]); } return storage; }, }; return new Proxy(construct, handle);}class Singleton { constructor(params) { this.val = params; } getVal() { console.log(this.val); } setVal(params) { this.val = params; }}// runlet ProxySingleton = createSingleton(Singleton);let singletonObj_1 = new ProxySingleton(123);let singletonObj_2 = new ProxySingleton(456);console.log(singletonObj_1 === singletonObj_2); // true复制代码
策略模式(Strategy pattern)
指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。
const Strategy = { A(params) { console.log('Execute strategy A!', params); }, B(params) { console.log('Execute strategy B!', params); }, C(params) { console.log('Execute strategy C!', params); },};const StrategyExecute = function(params) { const { type, val } = params; return Strategy[type](val);};// runStrategyExecute({ type: 'A', val: 123 });StrategyExecute({ type: 'B', val: 456 });StrategyExecute({ type: 'C', val: 789 });复制代码
代理模式(Proxy pattern)
为其他对象提供一个代理以控制对这个对象的访问。
let game = { play() { console.log('Play!'); },};let gameMachine = { token: 0, play: new Proxy(game.play, { apply(proxyTag, proxyThis, args) { if (proxyThis.token > 0) { proxyThis.token--; return Reflect.apply(proxyTag, proxyThis, args); } else { console.warn('No token!') } }, })};// rungameMachine.play(); // No token!gameMachine.token = 1;gameMachine.play(); //Play!gameMachine.play(); // No token!复制代码
迭代器模式(Iterator pattern)
提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。
// 迭代器function Iterator(params) { // 创建迭代器 let iterator = params(); // 开始迭代 let stepResult = iterator.next(); if (!stepResult.done) { // 定义下一步操作的方法,并执行 (function step() { if (!stepResult.done) { if (typeof stepResult.value === 'function') { (async function() { let stepResultData = await stepResult.value(); stepResult = iterator.next(stepResultData); step(); })(); } else { stepResult = iterator.next(stepResult.value); step(); } } })(); }}// 迭代步骤function fetchData() { // ajax get some data return [ { id: 1, name: 'test1', }, { id: 2, name: 'test2', } ];}function resolveData(params) { // rebuild data return params.map(item => item.name);}let iteratorList = function *() { const apiData = yield fetchData(); const list = yield resolveData(apiData); yield console.log(list);};// runIterator(iteratorList);复制代码
观察者模式(Observer pattern)
发布-订阅模式(Publish–subscribe pattern)
在对象间定义一个一对多的联系性,由此当一个对象改变了状态,所有其他相关的对象会被通知并且自动刷新。
let Event = { _events: {}, on(event, fn) { if (Array.isArray(event)) { event.forEach((e) => { this.on(e, fn) }); } else { (this._events[event] || (this._events[event] = [])).push(fn); } }, once(event, fn) { function on () { this.off(event, on); fn.apply(this, arguments); } on.fn = fn; this.on(event, on); }, off(event, fn) { if (!arguments.length) { this._events = Object.create(null); return true; } if (Array.isArray(event)) { event.forEach((e) => { this.off(e, fn) }); return true; } let eventFns = this._events[event]; if (!eventFns) { return false; } if (!fn) { this._events[event] = null; return true; } if (fn) { let eventFn; let len = eventFns.length; while (len--) { eventFn = eventFns[len]; if (eventFn === fn || eventFn.fn === fn) { eventFns.splice(len, 1); break; } } return true; } }, emit(event) { let eventFns = this._events[event]; if (eventFns) { let [ , ...rest ] = arguments; eventFns.forEach((fn) => { try { fn.apply(this, rest); } catch (err) { console.error(`Error: event handler for "${event}"`, err, this); } }); } }};// runEvent.once('testEventBus', () => { console.log('Event had emitted.'); });Event.emit('testEventBus'); // Event had emitted.复制代码
命令模式(Command pattern)
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。
class FanMachine { constructor() { this._commands = []; }; execute(command, ...rest) { this._commands.push(command); command.execute(...rest); }}class Fan { static turnOn() { console.log('开启'); }; static turnOff() { console.log('关闭'); }; static windSpeed(params) { console.log('风速:', params); };}class setCommand { constructor(fn) { this.execute = fn; }}// runlet fanCommandTurnOn = new setCommand(Fan.turnOn);let fanCommandTurnOff = new setCommand(Fan.turnOff);let fanCommandWindSpeed = new setCommand(Fan.windSpeed);let fanMachine = new FanMachine();fanMachine.execute(fanCommandTurnOn); // 开启fanMachine.execute(fanCommandWindSpeed, 1); //风速:1fanMachine.execute(fanCommandWindSpeed, 2); //风速:2fanMachine.execute(fanCommandWindSpeed, 3); //风速:3fanMachine.execute(fanCommandTurnOff); // 关闭复制代码
组合模式(Composite pattern)
把多个对象组成树状结构来表示局部与整体,这样用户可以一样的对待单个对象和对象的组合。
class Composite { constructor() { this._commands = []; } add(command) { this._commands.push(command); } execute() { this._commands.forEach((command) => { command.execute(); }) }}let commandA = { execute() { console.log('Execute A!'); }};let commandB = { execute() { console.log('Execute B!'); }};let commandC = { execute() { console.log('Execute C!'); }};let commandD = new Composite();commandD.add(commandB);commandD.add(commandC);let commandCenter = new Composite();commandCenter.add(commandA);commandCenter.add(commandD);// runcommandCenter.execute();// Execute A! // Execute B! // Execute C! 复制代码
模板方法模式(Template method pattern)
模板方法模式准备一个抽象类,将部分逻辑以具体方法及具体构造子类的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先构建一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。
class MakeDrink { constructor(name) { this._drinkName = name; } boilWater() { console.log('煮沸水'); } pourInCup() { console.log('加原料'); } brew() { console.log('冲泡'); } addCondiments() { console.log('加调料'); } init() { this.boilWater(); this.pourInCup(); this.brew(); this.addCondiments(); }}class MakeLemonTea extends MakeDrink { pourInCup() { console.log('加茶叶'); } addCondiments() { console.log('加柠檬和糖'); }}// runlet lemonTea = new MakeLemonTea('柠檬茶');lemonTea.init();// 煮沸水// 加茶叶// 冲泡// 加柠檬和糖复制代码
享元模式(Flyweight pattern)
通过共享以便有效的支持大量细颗粒对象。
// ...复制代码
职责链模式(Chain-of-responsibility Pattern)
为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。
class Chain { constructor(fn) { this._fn = fn; this._next = null; } setNext(next) { return this._next = next; } pass(...args) { let ret = this._fn.apply(this, args); if (ret === 'pass') { return this._next && this._next.pass.apply(this._next, args); } return ret; } next(...args) { return this._next && this._next.pass.apply(this._next, args); }}let first = new Chain(function(params) { console.log('first: ', params); this.next(2);});let second = new Chain(function(params) { console.log('second: ', params); return 'pass';});let third = new Chain(function(params) { console.log('third: ', params); this.next(4);});let last = new Chain(function(params) { console.log('last: ', params); return false;});first.setNext(second).setNext(third).setNext(last);// runfirst.pass(1);// first: 1// second: 2// third: 2// last: 4复制代码
中介者模式(Mediator pattern)
包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用,从而使它们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用,保证这些作用可以彼此独立的变化。
// ...复制代码
装饰器(修饰)模式(Decorator Pattern)
向某个对象动态地添加更多的功能。修饰模式是除类继承外另一种扩展功能的方法。
class Decorator { constructor(params) { const {value, configurable, enumerable, writable} = params; this.value = value && typeof value === 'function' ? value : null; this.configurable = configurable || false; this.enumerable = enumerable || false; this.writable = writable || false; } static create(obj, prop, descriptor) { if (descriptor.value && typeof descriptor.value === 'function') { let origin = obj[prop]; delete obj[prop]; descriptor.value = descriptor.value(origin); Object.defineProperty(obj, prop, descriptor); } }}let decoratorTest = { name: 'test', init() { Decorator.create(this, 'func', new Decorator({ value: function (fn) { return function () { // do something before... console.log('before!'); fn.apply(this, arguments); // do something after... console.log('after!'); } } })); console.log('init.'); }, func(...params) { console.log('decoratorTest: ', ...params); },};// rundecoratorTest.func(123);decoratorTest.init();decoratorTest.func(456);// decoratorTest: 123// init.// begin!// decoratorTest: 456// end!复制代码
状态模式(State pattern)
class State { constructor(params) { return State.create(this, params) } setState(state) { this.current = state; this.storage.push(state); } next() { const { name: currName, to: currTo } = this.main[this.current]; if (currTo) { const nextName = this.main[currTo].name; const {beforeLeave} = this.hock[currName]; const {beforeEnter, onMount} = this.hock[nextName]; if (beforeLeave) { beforeLeave(); } this.setState(currTo); if (beforeEnter) { beforeEnter(); } if (onMount) { onMount(); } } return this.current; } previous() { return this.goBack(1); } goBack(num) { const storage = this.storage; const len = storage.length; if (len > 1) { if (len > num) { this.current = storage[len - num -1]; this.storage = storage.slice(0, len - num); } else { this.current = storage[0]; this.storage = [storage[0]]; } } return this.current; } reset() { return this.goBack(this.storage.length - 1); } addHock(stateName, event, fn) { this.hock[stateName][event] = fn; } on(stateName, fn) { this.addHock(stateName, 'onMount', fn); } beforeEnter(stateName, fn) { this.addHock(stateName, 'beforeEnter', fn); } beforeLeave(stateName, fn) { this.addHock(stateName, 'beforeLeave', fn); } static create(_this, params) { const { initial, transitions, methods } = params; _this.current = initial; _this.storage = [initial]; _this.main = {}; _this.hock = {}; transitions.forEach((e) => { const { name, from, to } = e; // main _this.main[from] = {}; _this.main[from].name = name; _this.main[from].to = to; // hock _this.hock[name] = {}; _this.hock[name].from = from; _this.hock[name].to = to; _this.hock[name].beforeEnter = methods[`beforeEnter${State._firstUpperCase(name)}`] || null; _this.hock[name].onMount = methods[`on${State._firstUpperCase(name)}`] || null; _this.hock[name].beforeLeave = methods[`beforeLeave${State._firstUpperCase(name)}`] || null; // event _this[name] = function () { const currStateName = _this.main[this.current].name; if (name !== currStateName) { const { beforeLeave } = _this.hock[currStateName]; const { from, beforeEnter, onMount } = _this.hock[name]; if (beforeLeave) { beforeLeave(); } _this.setState.call(_this, from); if (beforeEnter) { beforeEnter(); } if (onMount) { onMount(); } return from; } else { return this.current; } } }); return _this; } static _firstUpperCase(str) { const [first, ...rest] = str; return `${first.toUpperCase()}${rest.join('')}`; }}const green = 'green';const yellow = 'yellow';const red = 'red';let fsm = new State({ initial: green, transitions: [ { name: 'greenLight', from: green, to: yellow, }, { name: 'yellowLight', from: yellow, to:red, }, { name: 'redLight', from: red, to: green, }, ], methods: { onGreenLight() { console.log('绿灯'); }, onYellowLight() { console.log('黄灯'); }, onRedLight() { console.log('红灯'); }, }});// run// 初始化状态为 greenfsm.next(); // 黄灯fsm.next(); // 红灯fsm.next(); // 绿灯fsm.previous(); // return 'red'fsm.greenLight(); // 绿灯fsm.yellowLight(); // 黄灯fsm.redLight(); // 红灯复制代码
设配器模式(Adapter pattern)
将某个类的接口转换成客户端期望的另一个接口表示。适配器模式可以消除由于接口不匹配所造成的类兼容性问题。
// ...复制代码