设计模式之单例模式
单例模式的定义是:保证一个类仅有一个实例,并提供一个访问它的全局访问点。也可以这样认为,单例只是全局的一个别称。
透明的单例模式
透明的单例模式:下面,我们将使用CreateDiv单例类,它的作用是负责在页面中创建唯一的div节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class CreateDiv { private static instance:CreateDiv private constructor(public html:string){ this.init() } public static createDivInstance(){ if(!this.instance){ this.instance = new CreateDiv('a div') } return this.instance } private init(){ const div = document.createElement('div') div.innerHTML = this.html document.body.appendChild(div) } }
let a = CreateDiv.createDivInstance() let b = CreateDiv.createDivInstance() console.log(a===b)
|
代理单例模式
在上段代码中,CreateDiv类实际上负责了两件事情。
第一:创建对象和初始化init方法;第二:保证只有一个对象
这不符合“单一职责原则”的概念,假如我们某天需要利用这个类,在页面中创建千千万万的div,即要让这个类从单例类变成一个普通的可产生多个实例的类,那我们必须得改写CreateDiv构造函数,把控制创建唯一对象的那一段去掉,这种修改会给我们带来不必要的烦恼。
现在我们通过引入代理类的方式,来解决上面提到的问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class CreateDiv {
constructor(public html: string) { this.init() } private init() { let div = document.createElement('div') div.innerHTML = this.html document.body.appendChild(div) }
}
class ProxySingletonCreateDiv {
private static instance: CreateDiv
public static getInstance(text: string) { if (!this.instance) { this.instance = new CreateDiv(text) } return this.instance } }
const a = ProxySingletonCreateDiv.getInstance('diva') const b = ProxySingletonCreateDiv.getInstance('divb') console.log(a == b)
|
惰性单例
惰性单例:是在需要的时候才创建对象实例。
实际上在文章开头就使用过这种技术,instance实例对象总是在我们调用CreateDiv.createDivInstance时候才被创建,上述都是基于”类“的单例模式,这在javascript中并不适用,我们应该使用函数+闭包的方式实现惰性单例。
下面以网页中的登录弹窗为例:
首先在用户进入网页时:如果登录弹窗一开始就被创建好,在用户只浏览不进行登录的情况下,这样会白白浪费一些DOM节点,只有用户点击登录按钮时才开始创建该弹窗;
还有就是该弹窗在页面是唯一的,不可能出现同时存在两个登录窗口的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| let createLoginLayer = (function(){ let div:HTMLDivElement; return function(){ if(!div){ div=document.createElement('div') div.innerHTML = '我是登录弹窗' div.style.display = 'none' document.body.appendChild(div) } return div } })()
let btn = document.getElementById('loginBtn') btn?.addEventListener('click',()=>{ let loginLayer = createLoginLayer(); loginLayer.style.display = 'block' })
|