单例模式的定义是: 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
任何只需要做一次的事情,都可以用单例实现。
单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏 览器中的 window 对象等。在 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.dir(MyApp);
// 上述代码等价于:
var MyApp = {
event: {},
dom: {
style: {}
}
};
使用闭包封装私有变量
var user = ( function () {
var __name = 'sven', __age = 29;
return {
getUserInfo: function () {
return __name + '-' + __age;
}
}
} )();
JavaScript 中的单例模式(惰性单例技术)
惰性单例指的是在需要的时候才创建对象实例。惰性单例是单例模式的重点,这种技术在实 际开发中非常有用
创建单例对象( 创建唯一实例对象)
//抽象管理单例的逻辑 单一职责【单例核心】
var getSingle = function( fn ){
var result;
return function(){
return result || ( result = fn .apply(this, arguments ) );
}
};
// 接下来将用于创建登录浮窗的方法用参数 fn 的形式传入 getSingle,我们不仅可以传入 createLoginLayer,还能传入 createScript、createIframe、createXhr 等。
// 创建登录浮窗
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";
};
// 这种单例模式的用途远不止创建对象,比如我们通常渲染完页面中的一个列表之后,接下来要给这个列表绑定 click 事件,如果是通过 ajax 动态往列表里追加数据,在使用事件代理的前提下,click 事件实际上只需要在第一次渲染列表的时候被绑定一次,但是我们不想去判断当前是否是第一次渲染列表,利用getSingle函数只绑定一个事件代码实现:
var bindEvent = getSingle(function() {
document.getElementById("div1").onclick = function() {
alert("click");
};
return true;
});
var render = function() {
console.log("开始渲染列表");
bindEvent();
};
render();
render();
render();
render 函数和 bindEvent 函数都分别执行了 3 次,但 div 实际上只被绑定了一个事件。
总结
在 getSinge 函数中,实际上也提到了闭包和高阶函数的概念。单例模式是一种简单但非常实 用的模式,特别是惰性单例技术,在合适的时候才创建对象,并且只创建唯一的一个。更奇妙的 是,创建对象和管理单例的职责被分布在两个不同的方法中,这两个方法组合起来才具有单例模 式的威力。