JS之设计模式介绍
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使用策略模式优化条件选择结构一文。