前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JS常用的几种设计模式

JS常用的几种设计模式

作者头像
Maic
发布2022-12-21 19:21:08
7130
发布2022-12-21 19:21:08
举报
文章被收录于专栏:Web技术学苑Web技术学苑

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

正文开始...

  • 单例模式

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

代码语言:javascript
复制

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

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

代码语言:javascript
复制

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

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

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

代码语言:javascript
复制

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);

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

代码语言:javascript
复制

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)
  • 装饰器模式

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

代码语言:javascript
复制

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方法

代码语言:javascript
复制

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

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

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

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

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

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

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

代码语言:javascript
复制

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('周末', '不上班')
  • 命令模式
代码语言:javascript
复制

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)
  • 模版模式
代码语言:javascript
复制

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();

如果还有一个类似的子类

代码语言:javascript
复制

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

我们再重新整合一下

代码语言:javascript
复制

const DrawTable = function(params){
	const {drawHeader, drawBody} = params
  const 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();
  • 适配器模式

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

代码语言:javascript
复制

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

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

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

代码语言:javascript
复制

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面向对象编程指南(第二版)》

最后,看完觉得有收获的,点个赞,在看,转发,收藏等于学会,欢迎关注Web技术学苑,好好学习,天天向上!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-12-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Web技术学苑 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档