简介看了这一章,手写了一个错误的例子:letSingleTip=function(content){this.content=content;this.showContent=function(){console.log(this.content);};这个.instance=null;this.getInstance=function(content){if(this.instance){returnthis.instance;}this.instance=newSingleTip(content);返回这个实例;};}//错误:SingleTip.getInstance不是函数//lettip1=SingleTip.getInstance('plzsitdown');//lettip2=SingleTip.getInstance('plzstandup');letsingleTip=newSingleTip();lettip1=singleTip.getInstance('请坐下');lettip2=singleTip.getInstance('请站起来');console.log(tip1===tip2);//测试是否相同一个例子这里的初衷是假设在某个网页中只允许存在一个tip。当我使用SingleTip.getInstance函数时,我收到一条错误消息,提示它不是函数。错误是getInstance的定义错误。该函数只能在实例化后使用。这不是很荒谬吗?单例模式的定义是保证一个类只有一个实例,并提供一个全局访问点来访问它。所以正确的姿势应该是通过类名SingleTip获取单个实例,而不是通过对象名singleTip。单例模式的简单例子在上面例子的基础上,修改getInstance的定义:letSingleTip=function(content){this.content=content;this.showContent=function(){console.log(this.content);};这个.instance=null;//原书例子,这里好像没必要}SingleTip.getInstance=function(content){if(this.instance){returnthis.instance;}this.instance=newSingleTip(content);returnthis.instance;}lettip1=SingleTip.getInstance('plzsitdown');lettip2=SingleTip.getInstance('plzstandup');console.log(tip1===tip2);//truehereexample代码没有实现内容修改。不管调用SingleTip.getInstance时传递的参数是什么,得到的实例都是第一次调用时得到的实例,内容总是'plzsitdown'。注意SingleTip.getInstance的函数体,这里this指向SingleTip(构造函数,本身也是一个对象),最后SingleTip的属性包括getInstance和instance。相反,实例属性“this.instance=null;”将放在tip1(对象实例)中,但这不是必需的。我们也可以把SingleTip.getInstance修改成立即执行函数的形式,并把它的返回结果做成一个函数,把实例放在函数闭包中:(我看不出有什么好处)letSingleTip=function(content){这个.content=内容;this.showContent=function(){console.log(this.content);};}SingleTip.getInstance=(function(content){letinstance;returnfunction(content){if(instance){returninstance;}instance=newSingleTip(content);returninstance;}})();lettip1=SingleTip.getInstance('plzsitdown');lettip2=SingleTip.getInstance('plzstandup');console.日志(提示1===提示2);一个更“透明”的单例实现了上面两个示例代码。问题是它依赖于getInstance来获取实例,这被称为“不够透明”。能否像普通类一样通过new关键字获取实例,在类定义中处理“单例”的限制?立即执行函数相当于一个相对独立的代码空间,可以进行相对独立的处理,最终返回外界需要的东西。这是一个_SingleTip的返回,由SingleTip包装,使用SingleTip类名来获取_SingleTip类的实例:return_instance;}this.init(content);_instance=this;return_instance;};//错误:this.init不是函数//注意_SingleTip.init还没有定义,不能提前调用,只有init可以放在对应原型上定义//_SingleTip.init=function(content){}_SingleTip.prototype.init=function(content){this.content=content;this.showContent=function(){log(this.content);};}return_SingleTip;})();lettip1=newSingleTip('plzsitdown');lettip2=newSingleTip('plzstandup');console.log(tip1===提示2);更重要的是,单例可以像普通类一样使用newSingleTip()正式获取,而且“单例”仅限于SingleTip内部,更加“透明”。从形式上看,代理模式实现的是单实例。在前面的例子中,最终使用立即执行函数将getInstance函数“组合”到SingleTip中,用newSingleTip()就可以得到单个实例。现在,我们重新进行“拆分”,目的是拆分上面例子中立即执行函数里面的两个步骤:创建对象,管理单例。letTip=(function(){let_Tip=function(content){this.init(content);};_Tip.prototype.init=function(content){this.content=content;this.showContent=function(){console.log(this.content);};}return_Tip;})();letSingleTipProxy=(function(){let_instance;returnfunction(content){if(_instance){return_instance;}_instance=新提示(content);return_instance;}})();lettip1=newSingleTipProxy('plzsitdown');lettip2=newSingleTipProxy('plzstandup');console.log(tip1===tip2);这样拆分,创建对象的代码在Tip,管理单例的代码在SingleTipProxy。Tip部分可以重复使用来创建普通对象;如果需要创建单例,请将其与SingleTipProxy结合使用。JavaScript中的单例模式JavaScript中的全局对象作为单例自然易于使用。因为:单例模式的核心是保证实例只有一个,提供全局访问。我们需要做进一步的限制,防止全局对象被覆盖和命名冲突,从而保证全局只有一个实例。通常有以下做法:使用命名空间JavaScript命名空间不是一个复杂的概念,使用对象字面量可以创建简单的命名空间:letnamespace1={name:'lucy',doSomething:function(){');}};letnamespace2={name:'nancy',doSomething:function(){console.log('DOSOMETHING');}};书中提到了一种动态创建命名空间的方法:varMyApp={};MyApp.namespace=function(name){varparts=name.split('.');varcurrent=MyApp;for(variinparts){if(!current[parts[i]]){current[parts[i]]={};}current=current[部分[i]];}};MyApp.namespace('event');MyApp.namespace('dom.style');console.dir(MyApp);//上面的代码等价于:varMyAppMyApp={event:{},dom:{风格:{}}};使用闭包封装变量来封装变量(名称)并暴露接口(getName、闭包引用、返回变量)供外部访问。letnamespace1=(function(){let_name='lucy';return{getName:function(){return_name;},doSomething:function(){console.log('dosomething');},}})();letnamespace2=(function(){let_name='nancy';return{getName:function(){return_name;},doSomething:function(){console.log('DOSOMETHING');},}})();
