js常用的几种设计模式

文章介绍了设计模式在实际业务中的应用,如单例模式用于全局实例,策略者模式处理条件判断,工厂模式创建相似对象,装饰器模式扩展功能,观察者模式实现发布订阅,命令模式处理事件,模版模式定制流程,适配器模式统一接口,以及高阶函数的before和after方法。强调了设计模式在特定场景下提升代码拓展性的重要性,同时也指出过度使用可能带来的阅读负担。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

设计模式

面试常常问到设计模式,设计模式在实际业务中即使有用到,但依然感受不到它的存在,往往在框架中会有更多体现,比如vue2源码,内部还是有很多设计思想,比如观察者模式,模版模式等,我们业务上一些通用的工具类也会用到单例,在大量的条件判断也会考虑策略者模式,这两种用得比较多。好记性不如烂笔头,又重新回顾了一遍设计模式,虽然仅仅掌握了几种熟悉的设计模式,但是希望在复杂的业务上,能想起那些不太常用的设计模式。

  • 单例模式

这种在业务里用得最多,常常是暴露一个全局的实例,或者是一个全局的类,所有的方法都是私有的,只能通过类访问内部方法

function Single () {if (!Single.instance) {
		Single.instance = this;}return Single.instance
}
const a = new Single()
const b = new Single()
 a === b; 
  • 策略者模式

我们通常在多个条件时,我们会考虑对象或者Map方式去替代我们的if条件,这是业务代码里常用的一种方式

const obj = {
	a: function() {},b: function() {}
}

const fn = (target) => {
	return (key) => target[key]()
}
const geta = fn(obj)('a');
const getb = fn(obj)('b'); 
  • 工厂模式

相似的对象,不知道应该用哪一种,根据输入的参数,自行决定创建指定类型的对象

var myApp = {};
myApp.dom = {};
myApp.dom.text = function(url) {
	this.url = url;this.insert = function(dom) {
	const text = document.createTextNode(this.url);
		dom.appendChild(text);
	}
}
myApp.dom.img = function(url) {
	this.url = url;this.insert = function(dom) {
	const img = document.createElement('image');img.src = this.url;
		dom.appendChild(img);
	}
}
const textObj = new myApp.dom.text('http://www.learn.wmcweb.cn');
textObj.insert(document.body);

const imgObj = new myApp.dom.img('http://www.learn.wmcweb.cn')
imgObj.insert(document.body); 

我们可以通过一个中间函数,通过形参去确定哪个对象

myApp.dom.factor = function(type, url) {return new myApp.dom[type](url)
}
const imgfator = myApp.dom.factor('img', 'http://www.learn.wmcweb.cn')
const textFator = myApp.dom.factor('text', 'http://www.learn.wmcweb.cn')
imgfator.insert(document.body);
textFator.insert(document.body) 
  • 装饰器模式

它是一种结构模型,与对象创建无关,如何拓展对象的功能

var person = {};
person.say = function() {  this.nowTime = function() {
	console.log('nowTime'); }
}
person.sleep = function() {this.curentTime = function() { console.log('this is current time'); }
}

person.getFn = function(type) {person[type].prototype = this;return new person[type]
}
person = person.getFn('say');

console.log(person.nowTime()) // ok
console.log(person.curentTime()) // 报错 

如果你像下面这样,就ok了。当你person.getFn('sleep')执行时就扩展了对象的功能,能访问绑定在自身属性属性的curentTime方法了,但是你会发现,此时也可以访问nowTime方法

person = person.getFn('say');
person = person.getFn('sleep');
console.log(person.nowTime()) // ok
console.log(person.curentTime()) // ok 
  • 观察者模式【发布订阅模式】

是一种行为模式,主要用于不同对象之间的交互信息

发布对象:重要事情发生时,会通知订阅者

订阅对象:监听发布对象的通知,并做出相应的反应

观察者主要分为两类:推送模式和拉动模式

推送模式是由发布者负责将消息发送给订阅者

拉动模式是订阅者主动跟踪发布者的状态变化

var observer = {};
observer.list = []; // 存放订阅者的回调函数

// 创建发布者
observer.trigger = function() {this.list.forEach(fn => {
	 fn.apply(this, arguments)
	})
}
// 创建订阅者
observer.addLinsten = function(fn) { this.list.push(fn)
}

observer.addLinsten(function(week, msg) {
		console.log(`今天${week}, ${msg}`)
})
observer.addLinsten(function(week, msg) {
		console.log(`今天${week}, ${msg}`)
})

observer.trigger('周末', '不上班') 
  • 命令模式
var btnClick = function(btn,callback) {btn.onclick = callback;
}
const modal = {open: function() {
	console.log('open');},close: function() {
		console.log('close');}
}
btnClick(document.getElementById('app'), modal.open)
btnClick(document.getElementById('app'), modal.close) 
  • 模版模式
var Table = function() {}
Table.prototype.drawHeader = function() {}
Table.prototype.drawBody = function() {}
Table.prototype.init = function() { this.drawHeader(); this.drawBody();
}
const createTable = new Table();
createTable.init(); 

如果还有一个类似的子类

var subTable = function() {}
subTable.prototype = new Table();
const stable = new subTable();
stable.init(); 

我们再重新整合一下

const DrawTable = function(params){
	const {drawHeader, drawBody} = paramsconst F = function() {};F.prototype.init = function() {
	drawHeader();
			drawBody();/* // or Object.keys(params).forEach(key => { params[key](); })*/
	}return F
}
const table = new DrawTable({
	drawHeader() {
			console.log('header');
	},drawBody() {
		console.log('body')
	}
});
table.init(); 
  • 适配器模式

通过一个统一的中间方法,做统一的适配调用

const render = (obj) => { obj.start();
}
const airplane = { start() {
			console.log('飞机开始飞了') }
}
const car = { start() {
	 console.log('火车开始了'); }
}

render(airplane)
render(car) 
  • 高阶函数

参考《javascript设计与开发实践》,下面afterbefore的设计很令人深思

Function.prototype.before = function(beforeFn) { const self = this; return function() { beforeFn.apply(this, arguments);// 先执行回调函数 return self.apply(this, arguments) // 然后执行原函数}
}
Function.prototype.after = function(afterFn) {const self = this;return function() {const ret = self.apply(this, arguments);afterFn.apply(this, arguments); // 执行当前的回调函数return ret;}
}
var func = function () {console.log(2)
}
func = func.before(()=> {
	console.log(1)
}).after(() => {
	console.log(3)
});
func(); // 1,2,3 

《javascript设计与开发实践》中也有很多其他模式,比如代理模式,中介者模式,状态模式等,很多的设计模式实际上在业务代码里并不会用到,在某些特殊业务场景这些设计模式的思想会大大增强我们代码的拓展性,但过度的设计模式也会带来一定的阅读负担,凡事不可追求两全其美,只需要适可而止。

总结

  • 常用的设计模式,比如说单例模式,单例就是只对外暴露一个实例,所有的内部方法都是通过这个实例访问
  • 策略者模式是一种多条件的优化模式,当你在条件判断很多时,可以考虑策略者模式
  • 工厂模式,主要通过一个中间函数,通过形参输出对应的对象
  • 装饰器模式,主要是扩展对象的多个功能能力
  • 观察者模式也是发布订阅模式,主要有发布对象与订阅对象,订阅者监听发布对象的通知做出响应,发布对象是有重要通知,统一通知所有订阅者
  • 另外看到一个利用闭包实现一个函数的before,after的例子
  • 本文示例主要参考《javascript设计与开发实践》《javascript面向对象编程指南(第二版)》

最后

最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值