1.单例模式

单例模式的定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

实现的方法为先判断实例存在与否,如果存在则直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。

适用场景:一个单一对象。比如:弹窗,无论点击多少次,弹窗只应该被创建一次。

class User{
constructor(name){
this.name = name
}
getName(){
return this.name
}
}

const Proxy = (function(){
let user = null
return function(name){
if(!user){
user = new User(name)
}
return user
}
})()

const u1 = Proxy('lyh')
const u2 = Proxy('fjh')
console.log(u1 == u2)

以上程序运算结果为true。

 

2.代理模式

代理模式的定义:为一个对象提供一个代用品或占位符,以便控制对它的访问。

常用的虚拟代理形式:某一个花销很大的操作,可以通过虚拟代理的方式延迟到这种需要它的时候才去创建(例:使用虚拟代理实现图片懒加载)

图片懒加载的方式:先通过一张loading图占位,然后通过异步的方式加载图片,等图片加载好了再把完成的图片加载到img标签里面。

<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>

</body>
<script type="text/javascript">
const loadingPic = 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fi0.hdslb.com%2Fbfs%2Farticle%2Fwatermark%2F17a5be8be1aa3f75acbab507617aa3e851030642.gif&refer=http%3A%2F%2Fi0.hdslb.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1669341629&t=3e651a0c04ff783cc83592e8345c32f1'
const killuaPic = 'https://img0.baidu.com/it/u=3308819396,1624138451&fm=253&fmt=auto&app=138&f=JPEG?w=700&h=393'

const imgFunc = (function(){
const img = document.createElement('img')
document.body.appendChild(img)
return {
setSrc: function(url){
img.src = url
}
}
})()

const imgProxy = (function(){
const img = new Image
img.onload = function(){
setTimeout(()=>imgFunc.setSrc(img.src),2000)
}
return {
setSrc: function(url){
imgFunc.setSrc(loadingPic)
img.src = url
}
}
})()

// imgFunc.setSrc(killuaPic)
imgProxy.setSrc(killuaPic)
</script>
</html>

使用代理模式实现图片懒加载的优点还有符合单一职责原则。减少一个类或方法的粒度和耦合度。

 

3.中介者模式

中介者模式,顾名思义,确实是需要有一位中介,也就是中间人,来将其他对象联系在一起,从而达到了解除其他对象与对象之间紧密耦合的关系。

举个例子,马上要过年了,年后我们需要走访各个亲戚家,什么七大姑八大姨的,数不胜数,关系乱七八糟,平日里经常往来的没几个,有些甚至连对方叫什么都不知道就拜年要红包。

这时,我们在想,要是有个中介来帮你走访亲戚,拜年要红包就好了,这样你就不在需要记住那么多的亲戚了,只需要通知中介一个人便可。这就是中介者模式的思想。

让我们来用代码实现一下。

分析:我们通过封装了一个类,将所有亲戚保存下来,这样就不用我们自己来记忆,只要通过暴露出来的方法,与中介沟通便可。

通过 add 函数来添加我们与某个亲戚之间的关系,如果某一亲戚不幸挂了我们可以通过 remove 将其从亲戚列表中删除,通过 payNewYear 来向亲戚 拜年要红包。

如果还有什么需求我们可以自己添加。

class Mediator{
constructor(){
this.relatives = {}
}

add(name,relation){
this.relatives[name] = relation
}

remove(name){
delete this.relatives[name]
}

payNewYear(name){
this.relatives[name] && console.log(`${this.relatives[name]},新年快乐,红包拿来`)
}
}

const mediator = new Mediator
mediator.add('小锋','叔叔')
mediator.add('大勇','伯伯')

mediator.payNewYear('小锋')
mediator.payNewYear('大勇')

mediator.remove('大勇')
mediator.payNewYear('大勇')

 

4.装饰者(装饰器)模式

定义为在不改变对象自身的基础上,在程序运行期间给对象动态地添加方法。

适用的场景:原有方法维持不变,在原有方法上再挂载其他方法来满足现有需求;

函数的解耦,将函数拆分成多个可复用的函数,再将拆分出来的函数挂载到某个函数上,实现相同的效果但增强了复用性。

例:用AOP装饰函数实现装饰者模式

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

function f(){
console.log(2)
}

function f1(){
console.log(1)
}

function f3(){
console.log(3)
}

f = f.before(f1).after(f3)
f()

 

5.工厂模式

工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类。该模式使一个类的实例化延迟到了子类。而子类可以重写接口方法以便创建的时候指定自己的对象类型。

简单说:假如我们想在网页面里插入一些元素,而这些元素类型不固定,可能是图片、链接、文本。

根据工厂模式的定义,在工厂模式下,工厂函数只需接受我们要创建的元素的类型,其他的工厂函数帮我们处理。

<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>

</body>
<script type="text/javascript">
class Text{
constructor(text){
this.text = text
}

insert(where){
const node = document.createTextNode(this.text)
where.appendChild(node)
}
}

class Link{
constructor(url){
this.url = url
}

insert(where){
const node = document.createElement('a')
node.innerText = this.url
node.href = this.url
node.target = '_blank'
where.appendChild(node)
}
}

class Img{
constructor(url){
this.url = url
}

insert(where){
const node = document.createElement('img')
node.src = this.url
where.appendChild(node)
}
}

// const text = new Text('lyh')
// text.insert(document.body)

// const link = new Link('http://www.baidu.com')
// link.insert(document.body)

// const img = new Img('https://img0.baidu.com/it/u=3308819396,1624138451&fm=253&fmt=auto&app=138&f=JPEG?w=700&h=393')
// img.insert(document.body)

class DomFactory{
constructor(type){
// this.type = type
return new (this[type]())
}

text(){
return Text
}

link(){
return Link
}

img(){
return Img
}
}

const linkFactory = new DomFactory('link')
linkFactory.url = ('http://www.baidu.com')
linkFactory.insert(document.body)

const imgFactory = new DomFactory('img')
imgFactory.url = ('https://img0.baidu.com/it/u=3308819396,1624138451&fm=253&fmt=auto&app=138&f=JPEG?w=700&h=393')
imgFactory.insert(document.body)
</script>
</html>

 

6.外观模式

定义:为子系统中的一组接口提供一个一致的界面,定义一个高层接口,这个接口使子系统更加容易使用

核心:可以通过请求外观接口来达到访问子系统,也可以选择越过外观来直接访问子系统

实现:外观模式在JS中,可以认为是一组函数的集合

function start(){
console.log('start')
}

function doing(){
console.log('doing')
}

function end(){
console.log('end')
}

//外观函数
function doSth(){
start()
doing()
end()
}

function init(){
doSth()
}
init()

 

7.策略模式

参考​​JS使用策略模式优化条件选择结构​​一文。