首页 > 代码库 > Javascript 设计模式 单例

Javascript 设计模式 单例

一直很喜欢Js,,,今天写一个Js的单例模式实现以及用法。

1、单例模式的写法

单例模式写法相当简单:

[javascript] view plaincopy在CODE上查看代码片派生到我的代码片
  1. var singleTon = {  
  2.        m1: "memeber first ",  
  3.        m2: "memeber second ",  
  4.        f1: function ()  
  5.        {  
  6.            console.log("fun1 ");  
  7.        }  
  8.    };  


好了,结束了,其实就是字面量创建对象的方式,很简单吧。如果你觉得单例太简单,不用看了,那你就错了,单例在Js中用的地方挺多,话说你经常用么~。

2、单例用法一:创建命名空间

在开发中一个页面一般会引入多个Js文件,甚至这多个文件多人写的,大家都可能在全局定义init这个方法,都可能在全局声明name这是属性。这样的话就造成的命名的冲突,发生一些意想不到的问题。所以我们需要引入命名空间:

我们可以让每个程序猿写的Js在他自己的命名空间下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * 创建命名空间 
  3.  * @type {{}} 
  4.  */  
  5. var ZhangHongYang = {};  
  6.   
  7. var zhy = {};  
  8. zhy.com = {} ;  
  9. zhy.com.js = {};  


比如以每个人的名字作为命名空间,之后写方法就:ZhangHongyang.xxx();或者你习惯了Java的命名空间,也可以zhy.com.js.xxx。

3、单例实例:实现一个注册页面的Js

针对像注册页面上的Js,一般都是针对此页面写的,建议使用单例的方式书写。

下面的展示如何使用单例的写法,实现ajax的注册功能,当然没有服务器,模拟一下:

html:

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <body>  
  2. <form action="user/register" id="registerForm">  
  3.   
  4.     <div>  
  5.         <label for="username">username</label>  
  6.         <input type="text" name="username" id="username"/>  
  7.     </div>  
  8.     <div>  
  9.         <label for="nickname">nickname</label>  
  10.         <input type="text" name="nickname" id="nickname"/>  
  11.     </div>  
  12.     <div>  
  13.         <label for="password">password</label>  
  14.         <input type="text" name="password" id="password"/>  
  15.     </div>  
  16.   
  17.     <div>  
  18.         <input type="submit" value=http://www.mamicode.com/"Register"/>  
  19.     </div>  
  20. </form>  
  21.   
  22. <div id="registerResult" style="width: 400px;height: 200px;border: 1px solid #444;">  
  23.   
  24. </div>  
  25.   
  26.   
  27. </body>  


当用户点击submit,会进行一些列的处理,最终将数据展示到registerResult中:

[javascript] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <script type="text/javascript">  
  2.   
  3.        /** 
  4.         * 单例的用法: 
  5.         * 有时候我们需要针对某个页面进行写Js,比如登录页面,建议使用下列方式: 
  6.         * ZhangHongyang,singlePageJsForRegiste = 
  7.         * { 
  8.         *     CONSTANT1:"", 
  9.         *     CONSTANT2:"", 
  10.         *     f1:function(){}, 
  11.         *     f2:function(){} 
  12.         * } 
  13.         */  
  14.   
  15.        ZhangHongYang.singlePageJsForRegister =  
  16.        {  
  17.            ID_FROM: "registerForm",  
  18.            ID_RESULT_CONTAINER: "registerResult",  
  19.            init: function ()  
  20.            {  
  21.                ZhangHongYang.singlePageJsForRegister.form = $("#" + this.ID_FROM);  
  22.                ZhangHongYang.singlePageJsForRegister.result = $("#" + ZhangHongYang.singlePageJsForRegister.ID_RESULT_CONTAINER);  
  23.                this.form.submit(this.handleSubmit);  
  24.            },  
  25.            handleSubmit: function (event)  
  26.            {  
  27.   
  28.                var datas = {};  
  29.                ZhangHongYang.singlePageJsForRegister.form.find("input").each(function (i)  
  30.                {  
  31.                    //omitted the unnecessary datas  
  32.                    if (!($(this).attr("type") == "button" || $(this).attr("type") == "submit" || $(this).attr("type") == "reset" ))  
  33.                    {  
  34.                        datas[$(this).attr("name")] = $(this).val();  
  35.                    }  
  36.                });  
  37.                ZhangHongYang.singlePageJsForRegister.ajaxSubmit(datas);  
  38.                //prevent the default form submit  
  39.                event.preventDefault();  
  40.            },  
  41.            ajaxSubmit: function (datas)  
  42.            {  
  43.                var url = ZhangHongYang.singlePageJsForRegister.form.attr("action");  
  44.                console.log("url :" + url);  
  45.                //make ajax submit here  
  46.                //$.post(url,datas,function(data){});  
  47.                //show result  
  48.                ZhangHongYang.singlePageJsForRegister.showResult(datas);  
  49.            },  
  50.            showResult: function (datas)  
  51.            {  
  52.                var result = "";  
  53.                for (var p in datas)  
  54.                {  
  55.                    result += p + " = " + datas[p] + "<br/>";  
  56.                }  
  57.                ZhangHongYang.singlePageJsForRegister.result.html(result);  
  58.            }  
  59.        };  
  60.   
  61.        $(function ()  
  62.        {  
  63.            ZhangHongYang.singlePageJsForRegister.init();  
  64.        });  
  65.   
  66.   
  67.    </script>  


我们使用单例定义了一个singlePageJsForRegister方法对象,然后将需要用到的元素的Id作为了常量,然后通过init初始化事件等,还有其他的几个函数,代码中也书写了注释。看了上面的代码可能觉得这么写好复杂,代码量也多了,但是对于Js的提升,要慢慢的学习面向对象以及结构化的写法,不能在script标签中,不断的定义各种方法,甚至在html标签中书写onclick这类的属性。Js一定要保证,html与js文件解耦;js代码整体上结构清晰;学习使用面向对象的方式处理问题。



4、如何在单例创建的对象中,定义私有方法和属性

上述单例的写法,会把所有的方法与变量暴露给使用者, 如何设置私有变量或者私有方法。

a、采用约定的方式:所有以_开头的方法和变量都是私有变量。

[javascript] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 
  2.        * 方式一 
  3.        * 采用约定,所有以_开头的变量或者方法为私有变量 
  4.        */  
  5.       var singleTon = {  
  6.           _m1: "hello",  
  7.           _f1: function ()  
  8.           {  
  9.           },  
  10.           init: function ()  
  11.           {  
  12.           }  
  13.       };  

 

可以觉得方式1不是自己骗自己么,但是项目嘛,约定由于配置,也是可行的。实在觉得不能忍受,看方式二:

[javascript] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 
  2.         * 方式二 
  3.         */  
  4.        var singleTon = (function ()  
  5.        {  
  6.            var _m1 = "hello";  
  7.            var _f1 = function ()  
  8.            {  
  9.                console.log(" i am a private function !");  
  10.            }  
  11.   
  12.            return {  
  13.                //public method  
  14.                init: function ()  
  15.                {  
  16.                    //invoke the private method in the singleTon  
  17.                    _f1();  
  18.                }  
  19.            };  
  20.   
  21.        })();  


采用了闭包的方式,很好的实现了私有变量和私有方法的隐藏。

5、单例实例:解决Textarea的数据存储时的Html转Txt和展示时Txt转Html

在web项目中,很多情况会使用到Textarea。

a、比如留言、技能的书写等;对于这类Textarea我们有必要对用户输入的html代码做特殊处理,防止用户填写恶意代码或者把页面的样式弄乱。

b、相反来说,在Textarea中书写的换行以及空格,最终在div中显示却没有效果,都是一个空格,所有很多web开发者会选择使用只读textarea来回显用户输入内容,其实需要做一定的转换。

html:

[javascript] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <body>  
  2. <textarea style="width: 400px;height: 120px;" id="taContent">  
  3. </textarea>  
  4.   
  5. <input type="button" id="convert" value=http://www.mamicode.com/"Convert"/>  
  6. <br/>  
  7. <br/>  
  8.   
  9.   
  10. <fieldset style="width: 400px">  
  11.     <legend>html转化为Txt,供Div展示</legend>  
  12. <div style="width: 400px;height: 120px;border: 1px solid #555;" id="divContent">  
  13.   
  14. </div>  
  15. </fieldset>  
  16.   
  17. <br/>  
  18. <br/>  
  19.   
  20. <fieldset style="width: 400px">  
  21.     <legend>Txt转化为Html,供Textarea修改</legend>  
  22.     <textarea style="width: 400px;height: 120px;" id="taEdit">  
  23.     </textarea>  
  24. </fieldset>  
  25.   
  26. </body>  


第一个Textarea用于用户输入,然后经过转义显示到div中,然后将转义后的数据进行逆向恢复显示到第二个TextArea中。相当与模拟了,div中展示数据和用户再次编辑数据,这些功能在项目中都相当实用。

我们的js代码:

[javascript] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 
  2.        * 对用户在TextArea中输入的数据进行过滤,把< -> <等操作,以及逆向操作 
  3.        */  
  4.       ZhangHongYang.htmlFilter = (function ()  
  5.       {  
  6.           /** 
  7.            * 转化textarea中的空格为$nbsp; 
  8.            * \n转化为<br/> 
  9.            * @private 
  10.            */  
  11.           function _transSpace(data)  
  12.           {  
  13.               return data.replace(/\n/g, "<br/>").replace(/\s/g, " ");  
  14.           };  
  15.   
  16.           /** 
  17.            * 转化所有尖括号 
  18.            * @private 
  19.            */  
  20.           function _transBrace(data)  
  21.           {  
  22.               return data.replace(/</g, "<").replace(/>/g, ">");  
  23.           };  
  24.   
  25.   
  26.           function _resumeSpace(data)  
  27.           {  
  28.               return data.replace(/ /g, " ").replace(/<br\s*\/>/ig, "\n");  
  29.           };  
  30.           function _resumeBrace(data)  
  31.           {  
  32.               return data.replace(/</g, "<").replace(/>/g, ">");  
  33.           };  
  34.   
  35.           return {  
  36.   
  37.               txt2Html: function (data)  
  38.               {  
  39.                   return _transSpace(_transBrace(data));  
  40.   
  41.               }, html2Txt: function (data)  
  42.               {  
  43.                   return _resumeSpace(_resumeBrace(data));  
  44.               }  
  45.           };  
  46.   
  47.       })();  


在我的命名空间下定义了htmlFilter方法,然后最后暴露两个方法Html2Txt和Txt2Html给使用者。

调用的代码:

[javascript] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <script type="text/javascript">  
  2.     $(function ()  
  3.     {  
  4.         $("#convert").click(function ()  
  5.         {  
  6.             var txt = ZhangHongYang.htmlFilter.txt2Html($("#taContent").val());  
  7.             console.log(txt);  
  8.             $("#divContent").html(txt);  
  9.             $("#taEdit").val(ZhangHongYang.htmlFilter.html2Txt(txt));  
  10.         });  
  11.     });  
  12. </script>  


效果图:


可以看到换行、空格、以及恶意的HTML代码等都得到了很好的在DIV中的显示;且最终可还原为Textarea中供编辑;如果各位项目中没有考虑到这类问题,首先你可以测试下问题,然后可以使用上面的代码解决这类问题。

6、单例写法提高多分支代码效率

相信大家都了解过ajax,对象ajax肯定离不开XMLHttpRequest,而且不同版本、类型的浏览器创建方式不一致。一般我们可能会这么写创建XMLHttpRequest的方法:

[javascript] view plaincopy在CODE上查看代码片派生到我的代码片
  1. function createXhr()  
  2.       {  
  3.           var xmlhttp;  
  4.           if (window.XMLHttpRequest)  
  5.           {// code for IE7+, Firefox, Chrome, Opera, Safari  
  6.               xmlhttp=new XMLHttpRequest();  
  7.           }  
  8.           else  
  9.           {// code for IE6, IE5  
  10.               xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");  
  11.           }  
  12.   
  13.           return xmlhttp ;  
  14.       }  


存在一个问题,每次创建XHR对象都需要进行分支判断,如果某个方法分支特别多,我们可以做进一步的优化,当浏览器加载js文件时,就决定以后调用只会用其中合适的方式,而不会走分支。

我们把代码改成:

[javascript] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 
  2.       * 用于在程序加载次js文件时,根据当前浏览器返回一个创建xhr的工厂方法,而不需要每次都去分支判断 
  3.       */  
  4.      ZhangHongYang.xhrFactroy = (function ()  
  5.      {  
  6.          function _ieCreateXhr()  
  7.          {     // code for IE6, IE5  
  8.              return   new ActiveXObject("Microsoft.XMLHTTP");  
  9.          }  
  10.   
  11.          function _newCreateXhr()  
  12.          {  
  13.              // code for IE7+, Firefox, Chrome, Opera, Safari  
  14.              return new XMLHttpRequest();  
  15.          }  
  16.   
  17.          if (window.XMLHttpRequest)  
  18.          {  
  19.              return _newCreateXhr;  
  20.          }  
  21.          else  
  22.          {  
  23.              return _ieCreateXhr;  
  24.          }  
  25.      })();  


当程序加载完成js文件后,会自动根据浏览器类型返回适合的方法,避免每次都会进行分支判断,我们只需要使用ZhangHongYang.xhrFactroy();创建XHR对象。

7、单例引入懒加载功能

上述的js的文件基本在引入页面后,浏览器加载就会进行大量操作占用内存,有时候我们希望等到我们去使用时再去执行一些操作,如果从未使用就省去不必要的内存消耗,我们可以进行如下改写代码:

[javascript] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 
  2.          * 用于在程序加载次js文件时,根据当前浏览器返回一个创建xhr的工厂方法,而不需要每次都去分支判断 
  3.          */  
  4.         ZhangHongYang.xhrFactroy = (function ()  
  5.         {  
  6.             var _instance = null;  
  7.   
  8.             function _constructor()  
  9.             {  
  10.                 function _ieCreateXhr()  
  11.                 {     // code for IE6, IE5  
  12.                     return   new ActiveXObject("Microsoft.XMLHTTP");  
  13.                 }  
  14.   
  15.                 function _newCreateXhr()  
  16.                 {  
  17.                     // code for IE7+, Firefox, Chrome, Opera, Safari  
  18.                     return new XMLHttpRequest();  
  19.                 }  
  20.   
  21.                 if (window.XMLHttpRequest)  
  22.                 {  
  23.                     return _newCreateXhr;  
  24.                 }  
  25.                 else  
  26.                 {  
  27.                     return _ieCreateXhr;  
  28.                 }  
  29.             }  
  30.   
  31.             return {getInstance: function ()  
  32.             {  
  33.                 if (_instance == null)  
  34.                 {  
  35.                     _instance = _constructor();  
  36.                 }  
  37.                 return _instance;  
  38.   
  39.             }};  
  40.   
  41.         })();  

 

[javascript] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <script type="text/javascript">  
  2.   
  3.         var xhrFactoryMethod = ZhangHongYang.xhrFactroy.getInstance();  
  4.         console.log(xhrFactoryMethod());  
  5.   
  6.     </script>  


只有使用时才会去执行_constructor()方法,而不是我们之前的一加载完成就执行。



好了,js的单例模式已经常用的方法介绍完了,以后在书写js代码时,可以尝试使用上述的方法进行书写,而不是大量定义全局function以及变量,请不要在html标签中增加事件处理的代码~

 

Javascript 设计模式 单例