单例模式应该是我最早接触的设计模式之一,尽管如此,前两天在写IFE的任务时还是懵比了好一阵,他喵的还是总结一下比较好。
PS:以下代码及相当一部分内容来源于 《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
或者:
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;
}
})();
这两种方法都是通过getInstance方法来获取唯一对象,不过缺点很明显,如果这个类使用者使用new方法创建实例,还是会有新的实例产生。
JS其实本身没有提供类的实现方法,当然在ES6中我们可以使用CLASS了,不过这只是语法糖而已,并非是新的继承模型,那我们其实可以搞一些JS特色的单例模式啊。
全局变量当然不是单例模式,但是我们其实常常这么用,比如var a = {};
,这样创建对象自然是独一无二的,不过全局变量容易造成命名空间污染,对此我们可以使用命名空间:
var namespace1 = {
a: function() {
alert(1);
},
b: function() {
alert(2);
}
};
或使用闭包:
var user = (function() {
var __name = 'sven',
__age = 29;
return {
getUserInfo: function() {
return __name + '-' + __age;
}
}
})();
惰性单例就是指在需要时才创建对象实例,上文的代码其实就是惰性单例了,但是实在是不够抽象,我们可以分离出真正interesting的部分:
var getSingle = function(fn) {
var result;
return function() {
return result || (result = fn.apply(this, arguments));
}
};
注意是创建对象的方法被当成参数传入getSingle函数,在让getSingle返回一个新的**函数!**并且用result返回fn的计算结果,因为result在闭包中,所以不会被销毁,一个完整的例子:
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';
};
这个方法是在这篇博客看到的,给人一种《逃生2》的断子绝孙之感:
function Person() {
//缓存实例
var instance = this;
//重写构造函数
Person = function() {
return instance;
}
//保留原型属性
Person.prototype = this;
//实例
instance = new Person();
//重置构造函数引用
instance.constructor = Person;
//其他初始化
instance.createTime = new Date();
return instance;
}
简单的来说就是构造函数第一次调用时,一开始就直接改写自身的构造函数令其将来执行时直接返回实例,再有条不紊地保留原型属性与构造函数引用(否则一旦对象被创建,其原型将被改变),最后再开始构造实例的工作,实在太优雅(而且感觉很疼)了[茶]。
诶~设计模式真是四国一啊ᕕ ( ᐛ ) ᕗ