一:单体模式简介:

  是什么:将代码组织为一个逻辑单元,这个单元中的代码通过单一的变量进行访问。只要单体对象存在一份实例,就可以确信自己的所有代码使用的是同样的全局资源。

  用途:1.用来划分命名空间,减少网页中全局变量的数目。

    2.在分支技术中用来封装浏览器之间的差异。

    3.单体对象创建的命名空间可以快速清除全局变量。

    4.增强模块性

  关于单体模式的好坏,等你看完所有的讲解之后再告诉你哦.......

二:单体结构:

1.最简单的单体就是一个对象字面量。

     var ProductTools={
findProduct: function(){
//.........
},
//其他类似的方法.....
} var resetProduct = $('rest-product-button');
var findProduct = $('find-product-button');

ProductTools中的方法不会被外面的所覆盖,它被装在自己创建的命名空间中。(金钟罩,铁布衫,哈哈)

接着可以把命名空间进行进一步的分割。为了避免与其他库代码冲突,可以定义一个用来包含自己所有代码的全局对象: 

     var GaintCrop = {};
GaintCrop.Common = {
//....拥有普通方法的单体,被用于所有的对象和方法。
}
GaintCrop.ErrorCodes = {
//....用来储存数据
}
GaintCrop.PageHandler = {
//....拥有特殊和私有页面方法的单体
}

2.用作特定网页专用代码的包装器的单体:

有些js代码用于多个页面,而有的只用于某个特定的网页,所以最好把这两种代码包装在自己的单体对象中。

接下来,举一个例子吧。常用的表单提交:

下面的单体会查找并劫持一个特定表单的提交:

     GaintCrop.RegPage = {
FORM_ID : 'reg-form',//要提交表单的ID
OUTPUT_ID : 'reg-results',//要显示提交成功后结果的元素的ID //....表单的处理方法....
handleSubmit: function(e){
e.preventDafault();//取消事件的默认动作 var data = {};
var inputs = GaintCrop.RegPage.formEl.getElementsByTagName('input'); //取出表单中input框输入的值
for(var i= 0, len = inputs.length; i < len; i++){
data[inputs[i].name] = inputs[i].value;
}
//把表单中的信息提交的服务器
GaintCrop.RegPage.sendRegistration(data);
},
sendRegistration: function(){
//发送请求到服务器(调用ajax),然后在成功函数里面调用
//......
},
displayResult: function(){
//显示服务器返回的data数据到页面中....
GaintCrop.RegPage.outputEl.innerHTML = response;
},
//初始化方法
init: function(){
//取出表单元素和要显示提交成功后结果的元素
GaintCrop.RegPage.formEl = $(GaintCrop.RegPage.FORM_ID);
GaintCrop.RegPage.outputEl = $(GaintCrop.RegPage.OUTPUT_ID); //劫持表单的提交
//参数:(dom对象,事件类型,触发函数)
addEvent(GaintCrop.RegPage.formEl, 'submit', GaintCrop.RegPage.handleSubmit)
}
}; //页面加载完成后调用 RegPage 的初始化方法。
addLoadEvent(GaintCrop.RegPage.init);

看到上面的代码是不是讲的很详细呢,是就点个头,表示一下赞同,真的是....

对于 GaintCrop 前面讲过了,在这里使用时它应该是已经作为一个空的对象字面量存在的(你存在....我存在...... - _ -)。为了以防万一可以添加如下代码:

     var GaintCrop = window.GaintCrop || {};

3.拥有私用成员的单体:

(1)用下划线表示

下面举一个例子:构造一个单体,干嘛用呢》....用来把string转换成数组

     GaintCrop.DataParse = {
//私有方法
_stripWhitespace: function(str){
return str.replace(/\s+/, '');//去掉空格
},
_stringSplit: function(str, delimiter){
return str.split(delimiter);//切割string,规则是delimiter
}, //公用方法
//stripWS 表示是否删除所有空格(一个布尔型的值)
stringToArray: function(str, delimiter, stripWS){
if(stripWS){
str = this._stripWhitespace(str);
}
var outputArray = this._stringSplit(str, delimiter);
return outputArray;
}
};

上面的两个方法作为私有方法提供给stringToArray方法使用。

仅仅是用下划线表示吗?......我去.........就是说带下划线就是私用的了 0.0 。当如果有一天你觉得某个带下划线的方法可以不用存在了,你就删了它,然后也不会对外面有影响,因为,一般也不会有人去调用你带下划线的方法,除非他S....B....  .真心不会有人这么调用。有的话,公司可以把它给辞掉了....(0.0有点严重,ha)

(2)使用闭包:

现在我们用一个在定义后立即执行的函数创建单体。

这个包装函数创建了一个可以用来添加真正的私用成员的闭包

     GaintCrop.DataParser = (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;
}
}
})();

GaintCrop.DataParser 获得的并不是一个函数,而是这个立即执行的方法返回的一个对象,这样返回的对象中的方法因为是在那个立即执行的函数中定义的,so......就可以调用所谓的私有方法和属性咯......这个闭包的概念不懂的去我前几节博客看一下吧....

4.惰性实例化:

什么是惰性实例化:这个吗>......前面讲了这么多...有一个共性,单体对象都是在脚本加载时被创建出来。对于资源密集型的或者配置开销大的单体,更合理的做法是将其实例化推迟到需要使用的时候。这种技术被称为惰性加载...lazy loading ,它最常用于那些必须加载大量数据的单体。而那些被用作命名空间、特定网页专用代码包装器或组织相关实用的方法的工具的单体最好还是立即实例化。

一个常规的单体转化成惰性的要分几步?(大象装冰箱分几步?....)

首先把单体的所有代码移到一个叫constructor的函数中:

         function constructor(){
//所有常规的单体的代码:
//私有方法和属性
var privateAttribute1 = false;
var privateAttribute2 = "123"; function privateMehtod1(){
//>....
}
function privateMehtod2(){
//>....
} return {
//公有属性和方法:
publicAttribute1: true;
publicAttribute2: 10;
publicMethod1: function(){
//....
};
publicMethod2: function(){
//....
}
}

这个方法外部不能访问,通过一个公用方法 getInstance 来访问:

     GaintCrop.Singleto = (function(){
var uniqueInstance;//用来判断单体是否实例化的私有属性 function constructor(){
//所有常规的单体的代码:
//.......
}
return {
getInstance: function(){
if(!uniqueInstance){ //如果单体没被实例话,就实例化单体
uniqueInstance = constructor();
}
return uniqueInstance;
}
}
})();

这样转换完后,要调用单体的公有方法,需要改变调用方式:GaintCrop.Singleto.getInstance().publicMethod1();

哎呀,调用个方法要写这么长。烦死.......(这就是惰性加载单体的缺点之一)。

 5.分支:

分支是一种用来把浏览器间的差异封装到运行期间进行设置的动态方法中的技术。(有点抽象吗?....看看下面的例子来理解吧)

举个例子,假如我们要创建一个返回XHR对象的方法,这种XHR对象在多数浏览器中石XMLHttpRequest类的实例,而在IE早期版本中则是某种ActiveX类的实例。这样一个方法通常会进行某种浏览器嗅探或者对象探测。如果不用分支技术,那么每次调用这个方法时,所有那些浏览器嗅探代码都要再次运行。要是这个方法的调用很频繁,这样做会严重缺乏效率。所有最好的办法是脚本加载时一次性地确定针对浏览器的代码。

用分支技术创建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');
}
}; 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('环境中未发现XHR对象....')
}
}
}
})();

这个单体可以用来生成XHR对象,使用这个API,只需要调用SimpleXhrFactory.createXhrObject() 就能得到适合特定的运行时环境的XHR对象。

这个单体创建了三个分支,通过一个变量testObject来测试是否是浏览器支持的XHR.

三:单体模式的利弊:

好处:

  1.组织作用:单体模式的主要好处在于它对代码的组织作用。把相关方法和属性组织在一个不会被多次实例化的单体中,可以使代码的调试和维护变得更轻松哦......

  2.节约内存:同第一条所讲,单体模式只会被实例化一次,节约内存。

  3.防止被误改:把方法包裹在单体中,可以防止它们被其他程序猿误改。

  单体模式的一些高级变体可以在开发后期用于对脚本进行优化,提高性能:

    使用惰性实例化技术,可以直到需要一个对象的时候才创建它,从而减少那些不需要它的用户承受的不必要的内存消耗(还可能包括带宽消耗)。

    分支技术可以用来创建高效的方法,不用管浏览器或者环境的兼容性。通过根据运行时的条件确定赋给单体变量的对象字面量,你可以创建出为特定环境量身定制的方法,这种方法不会在每次调用时都浪费时间去检查运行环境。

坏处:

  由于单体模式提供的是一种单点访问,所以它有可能导致模块间的强耦合。又因为这种强耦合导致它不利于单元测试。(你无法单独测试一个调用了来自单体的方法的类,只能把它和那个单体作为一个单元一起测试....)

  

JavaScript设计模式——单体模式的更多相关文章

  1. 读书笔记之 - javascript 设计模式 - 单体模式

    单体是一个用来划分命名空间,并将一批相关方法和属性组织在一起的对象,如果它可以被实例化,那么它只能被实例化一次. 单体模式,就是将代码组织为一个逻辑单元,这个逻辑单元中的代码可以通过单一的变量进行访问 ...

  2. javascript设计模式-单体模式

    场景:假设有一个Girl(美女)实体,该实体拥有姓名.年龄两个属性,拥有显示姓名和洗澡两个方法,下面分步骤构造该实体. 1.用简单基本单体模式: var Girl1 = { name:"昭君 ...

  3. 设计模式-单体模式(C++)

    设计模式-单体模式 单体模式在使用非常方便,适合于单一的对象,例如全局对象的抽象使用. 需要注意的是单体模式不可继承 // 实现 Singleton.h #ifndef __SINGLETON_H__ ...

  4. js设计模式--单体模式

    GOF里的23种设计模式, 也是在软件开发中早就存在并反复使用的模式. 如果程序员没有明确意识到他使用过某些模式, 那么下次他也许会错过更合适的设计 (这段话来自<松本行弘的程序世界>). ...

  5. javascript 设计模式-----策略模式

    在<javascript设计模式>中,作者并没有向我们介绍策略模式,然而它却是一种在开发中十分常见的设计模式.最常见的就是当我们遇到一个复杂的表单验证的时候,常常需要编写一大段的if和el ...

  6. JavaScript设计模式 - 迭代器模式

    迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示. 迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺 ...

  7. JavaScript设计模式 - 代理模式

    代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问 代理模式的用处(个人理解):为了保障当前对象的单一职责(相对独立性),而需要创建另一个对象来处理调用当前对象之前的一些逻辑以提高代码的效 ...

  8. javascript中单体模式的实现

    单体模式作为一种软件开发模式在众多面向对象语言中得到了广泛的使用,在javascript中,单体模式也是使用非常广泛的,但是由于javascript语言拥有其独特的面向对象方式,导致其和一些传统面向对 ...

  9. 读书笔记之 - javascript 设计模式 - 代理模式

    代理(proxy)是一个对象,它可以用来控制对另一对象的访问.它与另外那个对象实现了同样的接口,并且会把任何方法调用传递给那个对象.另外那个对象通常称为本体.代理可以代替本体被实例化,并使其可被远程访 ...

随机推荐

  1. 括号配对问题_栈<stack>

    问题 A: 括号配对问题 时间限制: 3 Sec  内存限制: 128 MB提交: 3  解决: 2[提交][状态][讨论版] 题目描述 现在,有一行括号序列,请你检查这行括号是否配对. 输入 第一行 ...

  2. js简单分页,可用

    //翻页调用 var pageSize = 1; var counts = 1; var current_page = 1; var rows,total; search(); //查询所有 func ...

  3. VS2010编译Qt4.8.2的64版本库

    安装qt-win-opensource-4.8.2-vs2010.exe(从http://download.qt.io/archive/qt/4.8/4.8.2/下 载),这个是32位的,里面有编译好 ...

  4. Collection 集合类

    ArrayList: 基于动态数组的List 它有两个重要的变量,size为存储的数据的个数.elementData 数组则是arraylist 的基础,因为他的内部就是通过这个数组来存储数据的. p ...

  5. linux初体验

    linux系统和window一样,也是一套独立的操作系统,它只是没有图形化界面而已

  6. Redis内存管理(一)

    Redis数据库的内存管理函数有关的文件为:zmalloc.h和zmalloc.c. Redis作者在编写内存管理模块时考虑到了查看系统内是否安装了TCMalloc或者Jemalloc模块,这两个是已 ...

  7. ld returned 1 exit status"的解决办法

    在Linux下创建线程时,编译时会出现下面的错误,[root@linuxserver 807]# gcc -o 22 22.c/tmp/cc21HcoW.o(.text+0x4c): In funct ...

  8. Android WebView 拦截自定义协议

    URL 语法 URL由三部分组成:资源类型.存放资源的主机域名.资源文件名. URL的一般语法格式为: (带方括号[]的为可选项): protocol :// hostname[:port] / pa ...

  9. KMP算法学习

    kmp算法完成的任务是:给定两个字符串O和f,长度分别为n和m,判断f是否在O中出现,如果出现则返回出现的位置.常规方法是遍历a的每一个位置,然后从该位置开始和b进行匹配,但是这种方法的复杂度是O(n ...

  10. Linux 通过os进程pid找到端口号

    [root@xxxx conf]# netstat -lutpn|grep tcp /java tcp /java tcp /java tomcat 监听的一些端口