const Singleton = function(name){ this.name = name this.instance = null } Singleton.getInstance = function(name){ if (!this.instance) { this.instance = new Singleton(name) } return this.instance } const a = Singleton.getInstance('mertens')
这个例子我们可以通过 Singleton.getInstance
来获取 Singleton
类的唯一对象,但有一个问题是这样增加了这个类的不透明性, Singleton
类的使用者必须知道这是一个单例类,并且跟以往通过 new XXX
的方式来获取对象不一样,这里需要用 Singleton.getInstance
来获取对象
const CreateDiv = (function(){ let instance const CreateDiv = function(html){ if (instance) { return instance } this.html = html this.init() return instance = this } })()
虽然上面的代码完成了一个透明的单例类的编写,但是它同样有一些缺点。
这段代码中, CreateDiv 的构造函数实际上做了两件事情
假设哪天我们需要利用这个类,把它变成一个普通的可以变成可以产生多个实例的类,我们就需要重写这个类。
现在我们通过引入代理类,来解决上面那个问题。
const CreateDiv = function(html){ this.html = html this.init() } const ProxySingletonCreateDiv = (function(){ var instance return function(html){ if (!instance) { instance = new CreateDiv(html) } return instance } })()
我们需要创建页面中唯一的 iframe
,或者 script
标签
const createIframe = (function(){ const iframe return function(){ if (!iframe) { iframe = document.createIframe('iframe') iframe.style.display = 'none' document.body.appendChild(iframe) } return iframe } })()
上面是创建一个 iframe,如果是创建一个 script 标签,套路也是差不多的
const obj if (!obj) { obj = xxx }
现在我们可以把通用的逻辑抽象出来
const getSingle = function(fn){ let result return function(){ return result || result = fn.apply(this, arguments) } } const createLoginLayer = function(){ const div = document.createElement('div') div.innerHTML = '我是登录浮窗' div.style.display = 'none' document.appendChild(div) return div } const createSingleLoginLayer = getSingle(createLoginLayer)
// 接下来,我们可以继续创建一个 iframe const createSingleIframe = function(){ const iframe = document.createElement('iframe') document.body.appendChild(iframe) return iframe } document.getElementById('loginBtn').onClick = () => { const LoginLayer = createSingleIframe() LoginLayer.src = 'http://www.xxx.com' }
在这个例子中,我们把创建实例对象的职责和管理单例的职责分别放置在两个方法里面,这两个方法可以独立变化而不影响,当他们连接在一起的时候,就完成了创建唯一实例对象的功能。