实现单例模式
单例模式的定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
单例模式的实现:主要通过创建一个标志变量来保存第一次创建过的实例,当下一次获取该类的实例时进行判断标志变量是否已经被赋值,如果已被赋值直接返回标志位上保存的值。
//第一种
var Singleton = function( name ){
this.name = name;
this.instance = null;
};
Singleton.prototype.getName = function(){
alert ( this.name );
};
Singleton.getInstance = function( name ){
if ( !this.instance ){
this.instance = new Singleton( name );
}
return this.instance;
};
var a = Singleton.getInstance( 'sven1' );
var b = Singleton.getInstance( 'sven2' );
alert ( a === b ); // true
//第二种
var Singleton = function( name ){
this.name = name;
};
Singleton.prototype.getName = function(){
alert ( this.name );
};
Singleton.getInstance = (function(){
var instance = null;
return function( name ){
if ( !instance ){
instance = new Singleton( name );
}
return instance;
}
})();
透明的单例模式
我们现在的目标是实现一个“透明”的单例类,用户从这个类中创建对象的时候,可以像使用其他任何普通类一样。在下面的例子中,我们将使用 CreateDiv 单例类,它的作用是负责在页面中创建唯一的 div 节点
var CreateDiv = (function(){
var instance;
var CreateDiv = function( html ){
if ( instance ){
return instance;
}
this.html = html;
this.init();
return instance = this;
};
CreateDiv.prototype.init = function(){
var div = document.createElement( 'div' );
div.innerHTML = this.html;
document.body.appendChild( div );
};
return CreateDiv;
})();
var a = new CreateDiv( 'sven1' );
var b = new CreateDiv( 'sven2' );
alert ( a === b ); // true
问题:破坏了“单一职责原理”CreateDiv的构造函数实际上负责了两件事情。第一件事创建对象和初始化init方法,第二件是保证只有一个对象。
使用代理模式实现单例模式
var CreateDiv = function( html ){
this.html = html;
this.init();
};
CreateDiv.prototype.init = function(){
var div = document.createElement( 'div' );
div.innerHTML = this.html;
document.body.appendChild( div );
};
接下来引入代理类 proxySingletonCreateDiv:
var ProxySingletonCreateDiv = (function(){
var instance;
return function( html ){
if ( !instance ){
instance = new CreateDiv( html );
}
return instance;
}
})();
var a = new ProxySingletonCreateDiv( 'sven1' );
var b = new ProxySingletonCreateDiv( 'sven2' );
alert ( a === b );
解决了上面的 “单一职责原理”
通用的惰性单例
现在我们就把如何管理单例的逻辑从原来的代码中抽离出来,这些逻辑被封装在 getSingle函数内部,创建对象的方法 fn 被当成参数动态传入 getSingle 函数接下来将用于创建登录浮窗的方法用参数 fn 的形式传入 getSingle,我们不仅可以传入还能传createScript、createIframe、createXhr 等。之之后再让 getSingle 返回一个新的函数,并且用一个变量 result 来保存 fn 的计算结果。result 变量因为身在闭包中,它永远不会被销毁。在将来的请求中,如果 result 已经被赋值,那么它将返回这个值。
var getSingle = function( fn ){
var result;
return function(){
return result || ( result = fn .apply(this, arguments ) );
}
};
var createLoginLayer = function(){
var div = document.createElement( 'div' );
div.innerHTML = '我是登录浮窗';
div.style.display = 'none';
document.body.appendChild( div );
return div;
};
var createSingleLoginLayer = getSingle( createLoginLayer );
document.getElementById( 'loginBtn' ).onclick = function(){
var loginLayer = createSingleLoginLayer();
loginLayer.style.display = 'block';
};
下面我们再试试创建唯一的 iframe 用于动态加载第三方页面:
var createSingleIframe = getSingle( function(){
var iframe = document.createElement ( 'iframe' );
document.body.appendChild( iframe );
return iframe;
});
document.getElementById( 'loginBtn' ).onclick = function(){
var loginLayer = createSingleIframe();
loginLayer.src = 'http://baidu.com';
};
getSingle函数中,用到了闭包和高阶函数的概念。单例模式是一种既简单又实用的模式,特别是惰性单例技术,再适合的时候才创建对象,并且只创建一个。更有意思的是创建对象和和管理单例职责被分布在不同的方法中,当两个方法组合起来的时候才会具备单例模式的特性。