《JavaScript设计模式》单例模式学习小结
一、解释
单例模式,又称单体(singleton)模式。单体是一个对象。具体来说是,用来划分命名空间,并将一批方法和属性组织起来的对象。它使代码成为一个逻辑单元,确保所有代码使用的都是同样的全局资源。
单体的基本结构如下:
var Singleton = {
username: '',
login: function () {
}
}
二、单体的使用
1. 用于划分命名空间的单体
单体对象内所有成员都被包装在这个对象中,所以它们不是全局的;由于这些成员只能通过单体对象变量进行访问,可以说它们统一存在于这一个命名空间内。
2. 在单体对象中创建私用成员
1)下划线表示私用成员
🌰举例:
myNamespace.Singleton = {
// 私有方法
_stripWhitespace: function (str) {
return str.replace(whitespaceRegex, '');
},
_stringSplit: function (str, delimiter) {
return str.split(delimiter);
},
// 公共方法
stringToArray: function (str, delimiter, stripWS) {
if (stripWS) {
str = stripWhitespace(str);
}
var outputArray = stringSplit(str, delimiter);
return outputArray;
}
}
2)使用闭包
🌰举例:
myNamespace.Singleton = (function () {
// 私有属性
var whitespaceRegex = /\s+/;
// 私有方法
function stripWhitespace(str) {
return str.replace(whitespaceRegex, '');
}
function stringSplit(str, delimiter) {
return str.split(delimiter);
}
return {
// 公共方法
stringToArray: function (str, delimiter, stripWS) {
if (stripWS) {
str = stripWhitespace(str);
}
var outputArray = stringSplit(str, delimiter);
return outputArray;
}
}
})();
⚡注意:下面两个写法创建的 myNamespace.Singleton 是一样的。匿名函数返回了一个对象。
myNamespace.Singleton = (function(){
return{
}
})();
myNamespace.Singleton = {}
3. 单体的惰性实例化
前面的单体模式的实现方式是立刻创建,但对开销大的单体,更合理的是在需要的时候加载,即惰性加载。惰性加载单体的访问需要借助静态方法:Singleton.getInstance.methodName 。getInstance 会检查单体是否被实例化,如果没有会创建并返回实例,否则直接返回现有实例。
🌰举例:把原来单体中的代码移到 constructor 方法中,这个方法不能从闭包外部访问。于是我们可以通过写一个公共方法 getInstance 来实现其调用时机的控制。
myNamespace.Singleton = (function(){
Var uniqueInstance;
Function constructor(){
// 私有属性
var whitespaceRegex = /\s+/;
// 私有方法
function stripWhitespace(str){
return str.replace(whitespaceRegex, '');
}
function stringSplit(str, delimiter){
return str.split(delimiter);
}
return{
// 公用方法
stringToArray: function(str, delimiter, stripWS){
if(stripWS){
str = stripWhitespace(str);
}
var outputArray = stringSplit(str, delimiter);
return outputArray;
}
}
}
return{
getInstance: function(){
If(!uniqueInstance){
uniqueInstance = constructor();
}
Return uniqueInstance;
}
}
})();
myNamespace.Singleton.getInstance().stringToArray()
⚡注意:被用于命名空间、或组织相关使用方法的工具的单体最好还是立即实例化。
4. 分支技术
前面的模式中,单体对象的所有代码都是在运行时确定的,也可以使用应变代码,这就用到了分支技术。
🌰举例:用分支技术创建XHR对象。用了分支技术后,嗅探代码只执行一次,而非每生成一个对象执行一次。
var SimpleXhrFactory = (function () {
var standard = {
createXhrObject: function () {
return new XMLHttpRequest();
}
};
var activeXNew = {
createXhrObject: function () {
return new ActiveXObject('Msxml2');
}
};
var activeXOld = {
createXhrObject: function () {
return new ActiveXObject('Microsoft');
}
};
var testObject;
try {
testObject = standard.createXhrObject();
return standard;
} catch (e) {
try {
testObject = activeXNew.createXhrObject();
return activeXNew;
} catch (e) {
try {
testObject = activeXOld.createXhrObject();
return activeXOld;
} catch (e) {
throw new Error('no xhr object found')
}
}
}
})();
⚡注意:使用的时候需要在计算的时间和占用的内存空间中做权衡。
三、单体模式的优缺点
1. 优点
- 把私有成员放在闭包中,确保不会在单体对象之外被使用;
- 可以自由改变对象的实现方式,对数据进行保护和封装且不会影响到别人的代码、第三方库代码;
- 避免重复引用逻辑混乱、性能消耗;
- 使用惰性实例化技术,直到需要对象的时候再创建,可以减少不必要的内存消耗;
- 使用分支技术,根据运行条件创建方法,且不会在每次调用都浪费时间去检查运行环境。
2. 缺点
- 可能导致模块间的强耦合,不利于单元测试。