单例模式
-
定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏 览器中的 window 对象等。在 JavaScript 开发中,单例模式的用途同样非常广泛。试想一下,当我 们单击登录按钮的时候,页面中会出现一个登录浮窗,而这个登录浮窗是唯一的,无论单击多少次登录按钮,这个浮窗都只会被创建一次,那么这个登录浮窗就适合用单例模式来创建。
-
实现一个单例模式
单例模式的实现并不复杂,无非是用一个变量来标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象。
-
代码:
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
JavaScript中的单例模式
基于类的单例模式在JavaScript中并不适用。全局变量不是单例模式,但在JavaScript中经常把全局变量当成单例模式来使用,但全局变量存在很多问题,很容易造成命名空间污染,以生命的变量很容易被别人覆盖。以下几种方式可以相对降低全局变量带来的命名污染:
- 使用命名空间:
var MyApp = {};
MyApp.namespace = function( name ){
var parts = name.split( '.' );
var current = MyApp;
for ( var i in parts ){
if ( !current[ parts[ i ] ] ){
current[ parts[ i ] ] = {};
}
current = current[ parts[ i ] ];
}
}
MyApp.namespace( 'event' );
MyApp.namespace( 'dom.style' );
console.log(MyApp)
//{namespace: ƒ, event: {}, dom: {style:{}}
-
使用闭包封装私有变量:
把一些变量封装在闭包的内部,只暴露一些接口跟外界通信,例如:
var user = (function(){ var __name = 'sven', __age = 29; return { getUserInfo: function(){ return __name + '-' + __age; } } })();
其中,用下划线来约定私有变量__name和__age,外界无法访问,避免来对全局的命令污染。
栗子
- 使用场景:webQQ页面,点击登录才弹出来登录框,而且不需要重复的创建,只创建一次,需要的时候拿出来就可以了,也就是单例模式。代码如下:
var createLoginLayer = (function(){
var div;
return function(){
if ( !div ){
div = document.createElement( 'div' );
div.innerHTML = '我是登录浮窗';
div.style.display = 'none';
document.body.appendChild( div );
}
return div;
}
})();
document.getElementById( 'loginBtn' ).onclick = function(){
var loginLayer = createLoginLayer();
loginLayer.style.display = 'block';
};
通用的惰性单例
上述代码虽然完成了一个可用的惰性单例,但他还存在两个问题:
- 这段代码仍然是违反单一职责原则的,创建对象和管理单例的逻辑都放在 createLoginLayer 对象内部
- 如果我们下次需要创建页面中唯一的 iframe,或者 script 标签,用来跨域请求数据,就 必须得如法炮制,把 createLoginLayer 函数几乎照抄一遍
于是我们把不变的部分提取出来,将创建单例的逻辑封装在getSingle函数内部:
var getSingle = function( fn ){
var result;
return function(){
return result || ( result = fn .apply(this, arguments ) );
}
};
//接下来无论要创建login登录框还是iframe,只要把创建函数写好传给getSingle函数就行了
var createLoginLayer = function(){
var div = document.createElement( 'div' );
div.innerHTML = '我是登录浮窗';
div.style.display = 'none'; 2 document.body.appendChild( div );
return div;
};
var createSingleLoginLayer = getSingle( createLoginLayer );
document.getElementById( 'loginBtn' ).onclick = function(){
var loginLayer = createSingleLoginLayer();
loginLayer.style.display = 'block';
};//创建iframe也一样