转载请标明出处:http://blog.csdn.NET/lmj623565791/article/details/30490955
一直很喜欢Js,,,今天写一个Js的单例模式实现以及用法。
1、单例模式的写法
单例模式写法相当简单:
[javascript] view plain copy
- var singleTon = {
- m1: "memeber first ",
- m2: "memeber second ",
- f1: function ()
- {
- console.log("fun1 ");
- }
- };
好了,结束了,其实就是字面量创建对象的方式,很简单吧。如果你觉得单例太简单,不用看了,那你就错了,单例在Js中用的地方挺多,话说你经常用么~。
2、单例用法一:创建命名空间
在开发中一个页面一般会引入多个Js文件,甚至这多个文件多人写的,大家都可能在全局定义init这个方法,都可能在全局声明name这是属性。这样的话就造成的命名的冲突,发生一些意想不到的问题。所以我们需要引入命名空间:
我们可以让每个程序猿写的Js在他自己的命名空间下:
[java] view plain copy
- /**
- * 创建命名空间
- * @type {{}}
- */
- var ZhangHongYang = {};
- var zhy = {};
- zhy.com = {} ;
- zhy.com.js = {};
比如以每个人的名字作为命名空间,之后写方法就:ZhangHongyang.xxx();或者你习惯了Java的命名空间,也可以zhy.com.js.xxx。
3、单例实例:实现一个注册页面的Js
针对像注册页面上的Js,一般都是针对此页面写的,建议使用单例的方式书写。
下面的展示如何使用单例的写法,实现ajax的注册功能,当然没有服务器,模拟一下:
html:
[html] view plain copy
- <body>
- <form action="user/register" id="registerForm">
- <div>
- <label for="username">username</label>
- <input type="text" name="username" id="username"/>
- </div>
- <div>
- <label for="nickname">nickname</label>
- <input type="text" name="nickname" id="nickname"/>
- </div>
- <div>
- <label for="password">password</label>
- <input type="text" name="password" id="password"/>
- </div>
- <div>
- <input type="submit" value="Register"/>
- </div>
- </form>
- <div id="registerResult" style="width: 400px;height: 200px;border: 1px solid #444;">
- </div>
- </body>
当用户点击submit,会进行一些列的处理,最终将数据展示到registerResult中:
[javascript] view plain copy
- <script type="text/javascript">
- /**
- * 单例的用法:
- * 有时候我们需要针对某个页面进行写Js,比如登录页面,建议使用下列方式:
- * ZhangHongyang,singlePageJsForRegiste =
- * {
- * CONSTANT1:"",
- * CONSTANT2:"",
- * f1:function(){},
- * f2:function(){}
- * }
- */
- ZhangHongYang.singlePageJsForRegister =
- {
- ID_FROM: "registerForm",
- ID_RESULT_CONTAINER: "registerResult",
- init: function ()
- {
- ZhangHongYang.singlePageJsForRegister.form = $("#" + this.ID_FROM);
- ZhangHongYang.singlePageJsForRegister.result = $("#" + ZhangHongYang.singlePageJsForRegister.ID_RESULT_CONTAINER);
- this.form.submit(this.handleSubmit);
- },
- handleSubmit: function (event)
- {
- var datas = {};
- ZhangHongYang.singlePageJsForRegister.form.find("input").each(function (i)
- {
- //omitted the unnecessary datas
- if (!($(this).attr("type") == "button" || $(this).attr("type") == "submit" || $(this).attr("type") == "reset" ))
- {
- datas[$(this).attr("name")] = $(this).val();
- }
- });
- ZhangHongYang.singlePageJsForRegister.ajaxSubmit(datas);
- //prevent the default form submit
- event.preventDefault();
- },
- ajaxSubmit: function (datas)
- {
- var url = ZhangHongYang.singlePageJsForRegister.form.attr("action");
- console.log("url :" + url);
- //make ajax submit here
- //$.post(url,datas,function(data){});
- //show result
- ZhangHongYang.singlePageJsForRegister.showResult(datas);
- },
- showResult: function (datas)
- {
- var result = "";
- for (var p in datas)
- {
- result += p + " = " + datas[p] + "<br/>";
- }
- ZhangHongYang.singlePageJsForRegister.result.html(result);
- }
- };
- $(function ()
- {
- ZhangHongYang.singlePageJsForRegister.init();
- });
- </script>
我们使用单例定义了一个singlePageJsForRegister方法对象,然后将需要用到的元素的Id作为了常量,然后通过init初始化事件等,还有其他的几个函数,代码中也书写了注释。看了上面的代码可能觉得这么写好复杂,代码量也多了,但是对于Js的提升,要慢慢的学习面向对象以及结构化的写法,不能在script标签中,不断的定义各种方法,甚至在html标签中书写onclick这类的属性。Js一定要保证,html与js文件解耦;js代码整体上结构清晰;学习使用面向对象的方式处理问题。
4、如何在单例创建的对象中,定义私有方法和属性
上述单例的写法,会把所有的方法与变量暴露给使用者, 如何设置私有变量或者私有方法。
a、采用约定的方式:所有以_开头的方法和变量都是私有变量。
[javascript] view plain copy
- /**
- * 方式一
- * 采用约定,所有以_开头的变量或者方法为私有变量
- */
- var singleTon = {
- _m1: "hello",
- _f1: function ()
- {
- },
- init: function ()
- {
- }
- };
可以觉得方式1不是自己骗自己么,但是项目嘛,约定由于配置,也是可行的。实在觉得不能忍受,看方式二:
[javascript] view plain copy
- /**
- * 方式二
- */
- var singleTon = (function ()
- {
- var _m1 = "hello";
- var _f1 = function ()
- {
- console.log(" i am a private function !");
- }
- return {
- //public method
- init: function ()
- {
- //invoke the private method in the singleTon
- _f1();
- }
- };
- })();
采用了闭包的方式,很好的实现了私有变量和私有方法的隐藏。
5、单例实例:解决Textarea的数据存储时的Html转Txt和展示时Txt转Html
在web项目中,很多情况会使用到Textarea。
a、比如留言、技能的书写等;对于这类Textarea我们有必要对用户输入的html代码做特殊处理,防止用户填写恶意代码或者把页面的样式弄乱。
b、相反来说,在Textarea中书写的换行以及空格,最终在div中显示却没有效果,都是一个空格,所有很多web开发者会选择使用只读textarea来回显用户输入内容,其实需要做一定的转换。
html:
[javascript] view plain copy
- <body>
- <textarea style="width: 400px;height: 120px;" id="taContent">
- </textarea>
- <input type="button" id="convert" value="Convert"/>
- <br/>
- <br/>
- <fieldset style="width: 400px">
- <legend>html转化为Txt,供Div展示</legend>
- <div style="width: 400px;height: 120px;border: 1px solid #555;" id="divContent">
- </div>
- </fieldset>
- <br/>
- <br/>
- <fieldset style="width: 400px">
- <legend>Txt转化为Html,供Textarea修改</legend>
- <textarea style="width: 400px;height: 120px;" id="taEdit">
- </textarea>
- </fieldset>
- </body>
第一个Textarea用于用户输入,然后经过转义显示到div中,然后将转义后的数据进行逆向恢复显示到第二个TextArea中。相当与模拟了,div中展示数据和用户再次编辑数据,这些功能在项目中都相当实用。
我们的js代码:
[javascript] view plain copy
- /**
- * 对用户在TextArea中输入的数据进行过滤,把< -> <等操作,以及逆向操作
- */
- ZhangHongYang.htmlFilter = (function ()
- {
- /**
- * 转化textarea中的空格为$nbsp;
- * \n转化为<br/>
- * @private
- */
- function _transSpace(data)
- {
- return data.replace(/\n/g, "<br/>").replace(/\s/g, " ");
- };
- /**
- * 转化所有尖括号
- * @private
- */
- function _transBrace(data)
- {
- return data.replace(/</g, "<").replace(/>/g, ">");
- };
- function _resumeSpace(data)
- {
- return data.replace(/ /g, " ").replace(/<br\s*\/>/ig, "\n");
- };
- function _resumeBrace(data)
- {
- return data.replace(/</g, "<").replace(/>/g, ">");
- };
- return {
- txt2Html: function (data)
- {
- return _transSpace(_transBrace(data));
- }, html2Txt: function (data)
- {
- return _resumeSpace(_resumeBrace(data));
- }
- };
- })();
在我的命名空间下定义了htmlFilter方法,然后最后暴露两个方法Html2Txt和Txt2Html给使用者。
调用的代码:
[javascript] view plain copy
- <script type="text/javascript">
- $(function ()
- {
- $("#convert").click(function ()
- {
- var txt = ZhangHongYang.htmlFilter.txt2Html($("#taContent").val());
- console.log(txt);
- $("#divContent").html(txt);
- $("#taEdit").val(ZhangHongYang.htmlFilter.html2Txt(txt));
- });
- });
- </script>
效果图:
可以看到换行、空格、以及恶意的HTML代码等都得到了很好的在DIV中的显示;且最终可还原为Textarea中供编辑;如果各位项目中没有考虑到这类问题,首先你可以测试下问题,然后可以使用上面的代码解决这类问题。
6、单例写法提高多分支代码效率
相信大家都了解过ajax,对象ajax肯定离不开XMLHttpRequest,而且不同版本、类型的浏览器创建方式不一致。一般我们可能会这么写创建XMLHttpRequest的方法:
[javascript] view plain copy
- function createXhr()
- {
- var xmlhttp;
- if (window.XMLHttpRequest)
- {// code for IE7+, Firefox, Chrome, Opera, Safari
- xmlhttp=new XMLHttpRequest();
- }
- else
- {// code for IE6, IE5
- xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
- }
- return xmlhttp ;
- }
存在一个问题,每次创建XHR对象都需要进行分支判断,如果某个方法分支特别多,我们可以做进一步的优化,当浏览器加载js文件时,就决定以后调用只会用其中合适的方式,而不会走分支。
我们把代码改成:
[javascript] view plain copy
- /**
- * 用于在程序加载次js文件时,根据当前浏览器返回一个创建xhr的工厂方法,而不需要每次都去分支判断
- */
- ZhangHongYang.xhrFactroy = (function ()
- {
- function _ieCreateXhr()
- { // code for IE6, IE5
- return new ActiveXObject("Microsoft.XMLHTTP");
- }
- function _newCreateXhr()
- {
- // code for IE7+, Firefox, Chrome, Opera, Safari
- return new XMLHttpRequest();
- }
- if (window.XMLHttpRequest)
- {
- return _newCreateXhr;
- }
- else
- {
- return _ieCreateXhr;
- }
- })();
当程序加载完成js文件后,会自动根据浏览器类型返回适合的方法,避免每次都会进行分支判断,我们只需要使用ZhangHongYang.xhrFactroy();创建XHR对象。
7、单例引入懒加载功能
上述的js的文件基本在引入页面后,浏览器加载就会进行大量操作占用内存,有时候我们希望等到我们去使用时再去执行一些操作,如果从未使用就省去不必要的内存消耗,我们可以进行如下改写代码:
[javascript] view plain copy
- /**
- * 用于在程序加载次js文件时,根据当前浏览器返回一个创建xhr的工厂方法,而不需要每次都去分支判断
- */
- ZhangHongYang.xhrFactroy = (function ()
- {
- var _instance = null;
- function _constructor()
- {
- function _ieCreateXhr()
- { // code for IE6, IE5
- return new ActiveXObject("Microsoft.XMLHTTP");
- }
- function _newCreateXhr()
- {
- // code for IE7+, Firefox, Chrome, Opera, Safari
- return new XMLHttpRequest();
- }
- if (window.XMLHttpRequest)
- {
- return _newCreateXhr;
- }
- else
- {
- return _ieCreateXhr;
- }
- }
- return {getInstance: function ()
- {
- if (_instance == null)
- {
- _instance = _constructor();
- }
- return _instance;
- }};
- })();
[javascript] view plain copy
- <script type="text/javascript">
- var xhrFactoryMethod = ZhangHongYang.xhrFactroy.getInstance();
- console.log(xhrFactoryMethod());
- </script>
只有使用时才会去执行_constructor()方法,而不是我们之前的一加载完成就执行。
好了,js的单例模式已经常用的方法介绍完了,以后在书写js代码时,可以尝试使用上述的方法进行书写,而不是大量定义全局function以及变量,请不要在html标签中增加事件处理的代码~
如果存在任何问题,或者有任何问题请留言~