单体模式
1. js最简单的单体模式
对象字面量:把一批有一定关联的方法和属性组织在一起
// 对象字面量
var Singleton = {
attr1: true,
attr2: 10,
method1: function() {},
method2: function(arg) {}
}
// 配合命名空间
var GiantCorp = {}
GiantCorp.Singleton = {...}
2. 拥有私有成员的单体
2.1 使用下划线
下面实现一个单体用于对字符串进行分割并返回分割后字符串组成的数组,它有三个参数:(要分割的字符串, 分割的标志字符串, [是否先对字符串进行去空格符处理]),这个例子下面也会用到
GiantCrop.DataParser = {
// private methods
_stripWitespace: function(str) { // 去空格符处理
return str.replace(/\s+/g, '');
},
_stringSplit: function(str, delimiter) { // 字符串分割处理
return str.split(delimiter);
},
// public method
stringToArray: function(str, delimiter, stripWS) {
if(stripWS) {
str = this._stripWitespace(str)
}
var outputArray = this._stringSplit(str, delimiter);
return outputArray
}
}
// 里面使用 this 来访问私有方法有一定风险,因为 this 不一定是指向 GiantCrop.DataParser,所以最好使用 GiantCrop.DataParser 来访问私有方法
2.2 使用闭包
GiantCrop.DataParser = (function() {
// private attribute
var whilespaceRegex = /\s+/g;
// private methods
function stripWitespace(str) {
return str.replace(whilespaceRegex, '');
}
function stringSplit(str, delimiter) {
return str.split(delimiter);
}
return {
// public method
stringToArray: function(str, delimiter, stripWS) {
if(stripWS) {
str = stripWitespace(str)
}
var outputArray = stringSplit(str, delimiter);
return outputArray
}
}
})()
3. 惰性实例化
GiantCrop.DataParser = (function() {
var uniqueInstance = null;
function constructor() { //控制实例化函数,外部无法访问到
// private attribute
var whilespaceRegex = /\s+/g;
// private methods
function stripWitespace(str) {
return str.replace(whilespaceRegex, '');
}
function stringSplit(str, delimiter) {
return str.split(delimiter);
}
return {
// public method
stringToArray: function(str, delimiter, stripWS) {
if(stripWS) {
str = stripWitespace(str)
}
var outputArray = stringSplit(str, delimiter);
return outputArray
}
}
}
return {
getInstance: function() {
if(!uniqueInstance) { // 判断是否已初始化实例
uniqueInstance = constructor()
}
return uniqueInstance
}
}
})()
// usage
GiantCrop.DataParser.getInstance().stringToArray('ss sabb bb', 'a', true)
4. 分支
在前端开发中,有时实现同一个功能需要去设配不同浏览器,比如进行 Ajax 请求,下面这个例子就是通过分支技术设配不同浏览器获得 XMLHttpRequest 对象
// 一起来就立即执行获得对应浏览器支持的 xhr 对象
var SimpleXhrFactory = (function() {
// 三个分支
var standard = {
createXhrObject: function() {
return new XMLHttpRequest();
}
}
var activeXNew = {
createXhrObject: function() {
return new ActiveXObject('Msxml2.XMLHTTP');
}
}
var activeXOld = {
createXhrObject: function() {
return new ActiveXObject('Microsoft.XMLHTTP')
}
}
// 使用 try catch 检查每个分支,并返回不报错的分支
var testObject = null;
try {
testObject = standard.createXhrObject();
return standard
}catch(err) {
try{
testObject = activeXNew.createXhrObject();
return activeXNew
}catch(err) {
try{
testObject = activeXOld.createXhrObject();
return activeXOld
}catch(err) {
throw new Error('No XHR object found in this environment')
}
}
}
})()
// usage
SimpleXhrFactory.createXhrObject()
5. 单体的利弊
1. 利
- 不会被多次实例化
- 使代码的调试和维护变的轻松
- 描述性的命名空间可以增强代码的自我说明性,有利于新手阅读和理解
- 可防止你的方法被其他程序员误改
- 使用惰性实例化技术可提升性能
- 使用分支技术可以创建出为特定环境量身定做的方法,这种方法不会在每次调用时再浪费时间去检查运行环境
2. 弊
- 单点访问,它有可能导致模块间的强耦合(有时创建一个可实例化的类比较可取,哪怕它只有被实例化一次)
- 强耦合不利于单元测试
- 当体最好留给定义命名空间和实现分支型方法这些用途
- 有时某些更高级的模式会更符合任务的需要,不能因为单体够用了就不适用其他模式
注意
转载、引用,但请标明作者和原文地址