JavaScript常见设计模式
单例模式
概念:这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。
作用:保证全局只有一个对象可以访问。
应用场景:想控制实例数目,节省系统资源的时候。Vue里的Render渲染器。
实现方式:饿汉式(一上来就实例化)和懒汉式(等到需要使用的时候才实例化)
class Singleton {
constructor() {}
}
Singleton.getInstance = (function() {
let instance
return function() {
if (!instance) {
instance = new Singleton()
}
return instance
}
})()
let s1 = Singleton.getInstance()
let s2 = Singleton.getInstance()
console.log(s1 === s2) // true
工厂模式
概念:工厂模式中,在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
作用:创建实例的复杂度,只需要提供一个接口,简单清晰。
应用场景:需要批量创建对象时
class Man {
constructor(name) {
this.name = name
}
alertName() {
alert(this.name)
}
}
class Factory {
static create(name) {
return new Man(name)
}
}
Factory.create('cxk').alertName()
代理模式
概念:代理模式是为了控制对对象的访问,不让外部直接访问到对象。
作用:转发请求,避免直接操作对象
应用场景:比如你需要买一件国外的产品,这时候你可以通过代购来购买产品。比如事件代理就用到了代理模式。Vue的数据劫持就应用了代理模式。
/* 事件代理 */
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
let ul = document.querySelector('#ul')
ul.addEventListener('click', (event) => {
console.log(event.target);
})
</script>
/* 对象代理 */
var girl = function (name) {
this.name = name;
};
// 委托人
var man = function (girl) {
this.girl = girl;
this.sendGift = function (gift) {
alert("Hi " + girl.name + ", dudu送你一个礼物:" + gift);
}
};
// 代理人
var proxyMan = function (girl) {
this.girl = girl;
this.sendGift = function (gift) {
(new dudu(girl)).sendGift(gift); // 替dudu送花咯
}
};
// 进行代理
var proxy = new proxyMan(new girl("酸奶小妹"));
proxy.sendGift("999朵玫瑰");
发布-订阅模式
概念:发布-订阅模式也叫做观察者模式。通过一对一或者一对多的依赖关系,当对象发生改变时,订阅方都会收到通知。
作用:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
应用场景:在 Vue 中,如何实现响应式也是使用了该模式。对于需要实现响应式的对象来说,在 get
的时候会进行依赖收集,当改变了对象的属性时,就会触发派发更新。
//发布者
class Publisher() {
construct() {
this.observers = []
}
//添加订阅者
add(oberver) {
this.observers.push(observer)
}
// 通知所有订阅者
notify() {
this.observers.forEach((observer) => {
//调用订阅者的函数
observer.update(this)
})
}
}
// 订阅者类 顾客
class Observer {
constructor() {
}
update() {
console.log('Observer buy buy buy')
}
}
const cunstomer = new Observer() //创建订阅者:顾客
const amazon = new Publisher()
amazon.add(cunstomer) //订阅到货小修消息
amazon.notify() //货物到达通知顾客
外观模式
概念:外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。
作用:降低访问复杂系统的内部子系统时的复杂度,简化客户端之间的接口。
应用场景:兼容浏览器添加事件的方法
function addEvent(elm, evType, fn, useCapture) {
if (elm.addEventListener) {
elm.addEventListener(evType, fn, useCapture)
return true
} else if (elm.attachEvent) {
var r = elm.attachEvent("on" + evType, fn)
return r
} else {
elm["on" + evType] = fn
}
}
装饰器模式
概念:给对象动态地增加职责的方式称为装饰器(decorator)模式
作用:在不改变对象自身代码的前提下,新增功能的模式。
应用场景:当我们接手老代码,需要对它已有的功能做个拓展。我们经常需要给手机戴个保护套防摔一样,不改变手机自身,给手机添加了保护套提供防摔功能。
var horribleCode = function(){
console.log(’我是一堆你看不懂的老逻辑')
}
var _horribleCode = horribleCode
horribleCode = function() {
_horribleCode()
console.log('我是新的逻辑')
}
horribleCode()
适配器模式
概念:不需要改变已有的接口,通过包装一层的方式实现两个接口的正常协作。
作用:适配器模式的作用是解决两个软件实体间的接口不兼容的问题。
应用场景:使用适配器模式之后,原本由于接口不兼容而不能工作的两个软件实体可以一起工作。比如Type-C和苹果的转接口。
class Plug {
getName() {
return '港版插头'
}
}
class Target {
constructor() {
this.plug = new Plug()
}
getName() {
return this.plug.getName() + ' 适配器转二脚插头'
}
}
let target = new Target()
target.getName() // 港版插头 适配器转二脚插头
时间戳转换为标准时间就是属于适配器模式
策略模式
概念:策略模式定义一系列的操作类型,把它们一个个封装起来, 并且使它们可相互替换。
作用:解决大量的if-else
场景
const strategy = {
s(base){
return base * 5;
},
a(base){
return base * 4;
},
b(base){
return base * 3;
},
c(base){
return base * 2;
}
}
const getBonus = (base, grade) => strategy[grade](base);
const p1 = getBonus(1000, 's') // 5000
const p1 = getBonus(1000, 'c') // 2000