是用一个变量来标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象。
代码如下:
var Singleton = function (name) {
this.name = name;
this.instance = null;
};
Singleton.getInstance = function (name) {
if (!this.instance) {
this.instance = new Singleton(name);
}
return this.instance;
};
var a = Singleton.getInstance('a');
var b = Singleton.getInstance('b');
console.log(a === b); // true
或者利用闭包:
var Singleton = function (name) {
this.name = name;
this.instance = null;
};
Singleton.getInstance = (function (name) {
var instance = null;
return function (name) {
if (!instance) {
instance = new Singleton(name);
}
return instance;
};
})();
var a = Singleton.getInstance('a');
var b = Singleton.getInstance('b');
console.log(a === b); // true
问题:
就是增加了这个类的“不透明性”,Singleton 类的使用者必须知道这是一个单例类
实现一个“透明”的单例类,用户从这个类中创建对象的时候,可以像使用其他任何普通类一样。
在下面的例子中,我们将使用 CreateDiv 单例类,它的作用是负责在页面中创建唯一的 div 节点,代码如下:
var CreateDiv = (function () {
var instance = null;
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('a');
var b = new CreateDiv('b');
console.log(a === b);
问题:
问题:假设我们某天需要利用这个类,在页面中创建千千万万的 div,即要让这个类从单例类变成一个普通的可产生多个实例的类,那我们必须得改写 CreateDiv 构造函数,把控制创建唯一对象的那一段去掉,这种修改会给我们带来不必要的烦恼
解决:
使用代理模式
在 CreateDiv 构造函数中,把负责管理单例的代码移除出去,使它成为一个普通的创建 div 的类:
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('a');
var b = new ProxySingletonCreateDiv('b');
console.log(a === b);
跟之前不同的是,现在我们把负责管理单例的逻辑移到了代理类 proxySingletonCreateDiv 中。这样一来,CreateDiv 就变成了一个普通的类,它跟 proxySingletonCreateDiv 组合起来可以达到单例模式的效果
在 JavaScript 开发中,我们经常会把全局变量当成单例来使用。
例如:
var a = {};
问题:
全局变量存在很多问题,它很容易造成命名空间污染
解决:
1、使用命名空间
var namespace1 = {
a: function(){
alert (1);
},
b: function(){
alert (2);
}
};
2、使用闭包封装私有变量
var user = (function(){
var __name = 'sven',
__age = 29;
return {
getUserInfo: function(){
return __name + '-' + __age;
}
}
})();
惰性单例指的是在需要的时候才创建对象实例
例子:
var createLoginLayer = (function () {
var div;
return function () {
if (!div) {
div = document.createElement('div');
div.innerHTML = '我是浮窗';
document.body.appendChild(div);
}
return div;
}
})()
// 用户点击按钮的时候才开始创建该浮窗
document.getElementById('loginBtn').onclick = function () {
var loginLayer = createLoginLayer();
};
问题:违反单一职责原则,创建对象和管理单例的逻辑都放在 createLoginLayer对象内部,如果我们下次需要创建页面中唯一的 iframe,或者 script 标签,用来跨域请求数据,就
必须得如法炮制,把 createLoginLayer 函数几乎照抄一遍
解决:第6点
将单例的逻辑从原来的代码中抽离出来,这些逻辑被封装在 getSingle函数内部,创建对象的方法 fn 被当成参数动态传入 getSingle 函数
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 = '我是浮窗';
document.body.appendChild(div);
return div;
}
var createSingleLoginLayer = getSingle(createLoginLayer);
document.getElementById('loginBtn').onclick = function () {
var loginLayer = createSingleLoginLayer();
};
也可以用在只执行一次的函数
比如:给一个div绑定事件
var bindEvent = getSingle(function () {
console.log('给div绑定事件~');
return true;
});
bindEvent();
bindEvent();
bindEvent();
单例模式是一种简单但非常实用的模式,特别是惰性单例技术,在合适的时候才创建对象,并且只创建唯一的一个。更奇妙的是,创建对象和管理单例的职责被分布在两个不同的方法中,这两个方法组合起来才具有单例模式的威力。