1、null)newSingleton();returninstance;这样只有在第一次实例化的时候,才创建对象;通过静态方法,得到唯一实例;这个是C#中最简单的单件模式写法。而Javascript作为弱类型语言,有着它独特的地方,现在我就来介绍Javascript单件模式的几种形式:1. 最基本的单件模式varLoginUsername:匿名用户,sex保密setNamefunction(name)this.namename;,setSexfunction(sex)this.sexsex;getUserInfofunction()用户名:+;性别:+this.sex;这里定义了一个对象(Log
2、inUser),对象中包含了各种属性(name,sex)和方法(setName,setSex,getUserInfo);这样我新建一个HTML页面:window.onloadalert(LoginUser.getUserInfo();LoginUser.setName(Leepy);LoginUser.setSex(男/alert(LoginUser.getUserInfo();functiontest()/scriptinputbuttonvalue=testonclick=test();/可以发现,界面初始化时弹出的警告框为“用户名:匿名用户;保密”,通过setName和setSex方法之
3、后,点击按钮后弹出的警告框为“用户名:Leepy;男”,说明在不同的方法作用域下,LoginUser保持着修改后的状态,因此LoginUser在页面中就保持着单一的状态。我想大家一定也听过prototype的JS框架了吧,最新版本为实际上在它的文件中包含着很多这样类似的代码,比如从文件一开头就可以发现:PrototypeVersion:1.6.0.3,Browser:IE:!(window.attachEvent&navigator.userAgent.indexOf(Opera)=-1),Opera:-1,WebKit:AppleWebKit/Gecko:Gecko-1KHTMLMobile
4、Safari:navigator.userAgent.match(/Apple.*Mobile.*Safari/),BrowserFeatures:XPath:document.evaluate,SelectorsAPI:document.querySelector,ElementExtensions:window.HTMLElement,SpecificElementExtensions:document.createElement(div)_proto_=formScriptFragment:script*(Ss*?)JSONFilter:/*-secure-(sS*)*/s*$/,emp
5、tyFunction:K:function(x)x;/以下是它的使用(Prototype.Browser.MobileSafari)Prototype.BrowserFeatures.SpecificElementExtensionsfalse;可以看出,Version、Browser、BrowserFeatures、ScriptFragment、JSONFilter作为Prototype对象的属性,而emptyFUnction、K作为Prototype对象的方法;因此它就是一个最基本的单件模式。由于Javascript的语言特性,可以在后期动态添加,删除,修改属性:如:LoginUser.a
6、ge = 24; 那么LoginUser对象便增加了age属性;而如:delete LoginUser.name;那么LoginUser对象就删除了name属性;LoginUser.name = cnblogs;那么LoginUser对象的“私有”属性不需要通过setName的“公有”方法仍然能够做出修改。因为根据设计模式原则:对扩展开放而对修改关闭,显然违背了该条准则。为了防止这种情况的发生,到时候会引入闭包的方式,稍后会说明。2. 命名空间的单件模式命名空间可以很好地划分 属性和方法 的归属,以及可以防止 属性和方法 被轻易的修改,通过访问各自的命名空间得到对应我们想要的 属性和方法。这里
7、还是以上面的LoginUser为例:LoginUser.Mother母亲姓名career职位setCareerfunction(career)this.careercareer;LoginUser.name的母亲名字:职业:+this.career;从代码中看出,这里我把LoginUser作为“命名空间”,而LoginUser.Mother作为它的一个“全局变量”,这样做的好处可以防止LoginUser的属性和方法被轻易地覆盖,通过LoginUser.,以致于LoginUser.(如LoginUser.Mother.Brother)来划分 属性和方法 的归属,如LoginUser中的name属
8、性和LoginUser.Mother中的name属性是区分开来的。LoginUser.Mother.setName(adminLoginUser.Mother.setCareer(农民alert(LoginUser.Mother.getUserInfo();可以得到下面的弹出框:LoginUser和LoginUser.Mother的name属性已经区分开来了。在prototype.js文件中也用到命名空间的单件模式:Classcreate:parentnull,properties$A(arguments);(Object.isFunction(properties0)properties.s
9、hift();klass()this.initialize.apply(this,arguments);Object.extend(klass,Class.Methods);klass.superclassparent;klass.subclasses;(parent)subclasssubclass.prototypeparent.prototype;klass.prototypesubclass;parent.subclasses.push(klass);for(vari0;properties.length;i+)klass.addMethods(propertiesi);(!klass
10、.prototype.initialize)klass.prototype.initializePrototype.emptyFunction;klass.prototype.constructorklass;Class.MethodsaddMethods:function(source)ancestorthis.superclassthis.superclass.prototype;Object.keys(source);Object.keys(toString:true).length)properties.push(toStringvalueOf0,lengthlength;i+)pro
11、pertypropertiesi,valuesourceproperty;(ancestorObject.isFunction(value)value.argumentNames().first()$supermethodvalue;(function(m)ancestorm.apply(this,arguments)(property).wrap(method);value.valueOfmethod.valueOf.bind(method);value.toStringmethod.toString.bind(method);this.prototypepropertythis;这里实际上
12、Class.create实现的是类的继承,具体这里我就不再阐述了,大家可以查看prototype官方的Api文档。3. 闭包方式的单件模式如果要得到真正意义上的“私有”成员,那么闭包方式是构造单件模式的一种选择。通过闭包的方式,只暴露一些可以公开的方法或者属性,而私有成员只在内部实现操作,而所有的属性和方法只需要实例化一次。现在开始继续看LoginUser的例子,闭包方式单件模式(左)对比第1条基本单件模式(右)的例子:(function()_name_sexfunction()_sex;getName_name;)();this._namethis._sex+this._sex;可以发现,闭
13、包方式将公共的方法放在return . 中,而属性_name和_sex做为参数传入return . 中;现在两种方式都实现一下代码,测试一下:alert(LoginUser._name);可以得到闭包方式单件模式(左)对比第1条基本单件模式(右)如下两个结果:可以看出闭包方式的LoginUser无法得到_name的值,而基本方式的LoginUser可以得到_name的值;这进一步说明了闭包方式的_name已经成为“私有”成员属性了。而如果要得到_name的值,只有通过公开方法或者公开属性来获得,如下:/注意这里的“”号不能够换行到下一行,不然浏览器提示错误这样子,alert(LoginUser
14、.getName();就可以显示正确的值了。继续看prototype.js文件中,其实也用到闭包方式的单件模式:HashClass.create(Enumerable,(function()toQueryPair(key,value)(Object.isUndefined(value)key;key=encodeURIComponent(String.interpret(value);initialize:function(object)this._objectObject.isHash(object)?object.toObject()Object.clone(object);_each:f
15、unction(iterator)inthis._object)this._objectkey,pairkey,value;pair.keypair.valueiterator(pair);set:function(key,this._objectkeyget:function(key)simulatingpoorlysupportedhasOwnProperty(this._objectkeyObject.prototypekey)this._objectkey;unset:deletetoObject:Object.clone(this._object);keys:this.pluck(k
16、eyvalues:valueindex:function(value)matchthis.detect(function(pair);match.key;merge:this.clone().update(object);update:Hash(object).inject(this,function(result,pair)result.set(pair.key,pair.value);result;toQueryString:this.inject(,function(results,encodeURIComponent(pair.key),valuespair.value;(valuestypeofobject(Object.isArray(values)results.concat(values.map(toQueryPair.curry(key);elseresult