javascript设计模式之单体模式

单体是一个用来划分命名空间并将一批相关的属性和方法组织在一起的对象,如果他可以被实例化,那么他只能被实例化一次。

单体模式是javascript里面最基本但也是最有用的模式之一。

特点:

1.       可以来划分命名空间,从而清除全局变量所带来的危险。

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

3.       可以把代码组织的更为一体,便于阅读和维护。

单体的基本结构(正确写法):

/* Basic Singleton */
var  Singleton  =  {

         attribute1:
true ,

         attribute2:
10 ,

         method1:
function (){},

      method2: function (){}

};

 

1划分命名空间:

 1  var  box  =  {
 2          width: 0 ,
 3          height: 0 ,
 4          getArea: function (){
 5               return   this .width * this .height; // js中对象成的访问必须是显示的,即this是不能省略的
 6          },
 7          init: function (w,h){
 8           //     width = w;
 9           //    height = h;这种方式相当于定义了两个全局变量,(没加var声明的变量为全局变量)
10           //     并不是对对象width和height的赋值
11           // 下面是正确的
12               this .width  =  w;
13               this .height  =  h;
14          }
15      } // box划分了一个命名空间,命名空间里的变量只在空间里有效
16 
上面的单体中的所有的成员以及方法都是公有的(public),也就是在单体的外部可以对他们进行任意的改动,那为什么说单体提供了一个命名空间呢?

 

我们继续:

 1  var  box  =  {
 2          width: 0 ,
 3          height: 0 , // 单体的变量
 4          getArea: function (){
 5               return  width * height; // 中的,width,height其实并不是单体的变量,而是在init中定义的全局变量
 6          }
 7          init: function (w,h){
 8              width  =  w;
 9              height  =  h;
10          }
11      } // init中width,height其实并不是单体的变量
12  window.onload  =   function (){
13           var  init  =  box.getArea();
14      alert(init);
15  }
由于没有对init中的width,height进行初始化,所以会报错,这样改一下:

 1  var  box  =  {
 2          width: 0 ,
 3          height: 0 ,
 4          getArea: function (){
 5               return  width * height;
 6          },
 7          init: function (w,h){
 8              width  =  w;
 9              height  =  h;
10          }
11      }
12  window.onload  =   function (){
13          width  =   0 ;
14          height  =   0 ;
15           // or box.init(0,0);
16           var  init  =  box.getArea();
17      alert(init);
18  }
发现可以了,由于init和 getArea所用的width和height并不是归单体所有的变量,而是一个全局变量,所以我们可以在单体外面进行随意调用而不受影响

 

如果我们这样写一下就更明白了:

 1  var  box  =  {
 2          width: 0 ,
 3          height: 0 ,
 4          getArea: function (){
 5               return  width * height; // js中对象成的访问必须是显示的,即this是不能省略的
 6          },
 7          init: function (w,h){
 8              width  =  w;
 9              height  =  h;
10          }
11      } // 这里的width,height其实并不是单体的对象
12  window.onload  =   function (){
13          width  =   0 ;
14          height  =   0 ;
15           var width = box.getArea();
16      alert(width);
17  }
这样写又会报错了,可见我们以上的方式对于全局变量并没有建立起一个命名空间,全局变量为我们带来了危险。所以最上面的写法是对的,我们来验证一下:

 1  var  box  =  {
 2          width: 2 ,
 3          height: 2 ,
 4          getArea: function (){
 5               return   this .width * this .height; // js中对象成的访问必须是显示的,即this是不能省略的
 6          },
 7          init: function (w,h){
 8               this .width  =  w;
 9               this .height  =  h;
10          }
11      }
12  window.onload   =   function (){
13          width  =   0 ;
14          height  =   0 ;
15           var  width  =  box.getArea();
16      alert(width);
17  }
可见在 window.onload中的width 和height已经没有干扰了,因为单体为单体中的width和height建立了一个命名空间。

 

2成员的属性:

讨论完命名空间,我们来对单体变量和方法的属性做一下设定。学过其他语言的人(java,c++,c#...)都应该很了解其中类成员的public和private,

虽然在javascript中没有这么严格的面向对象(oop),但是我们可以借助闭包来进行一个模仿,毕竟有的变量设为public是很不好的。

 1  var  circle  =  ( function (){
 2  // pravite member!
 3       var  r  =   5 ;
 4       var  pi  =   3.1416 ; // 后面用分号
 5       return { // public member
 6          getArea: function (){
 7               return  r * r * pi; // 访问私有成员不要加this
 8          }, // 后面用逗号
 9           // 如果想改变r和pi的值,只能通过设置一个公有的函数来实现
10          init: function (setR){
11              r  =  setR;
12          }
13      }
14  })()
15  window.onload  =   function (){
16      circle.r  =   0 ; // 无法访问私有成员,相当于又为circle创建了一个共有成员r
17      alert(circle.getArea());    
18      circle.init( 0 ); // 通过公有的工具函数便可以访问了。
19      alert(circle.getArea());    
20  };
私有变量、方法是只读的,公有变量、方法是可读可写的
访问:
对于私有成员,直接访问即可,前面不用加任何修饰,
对于公有的访问在单体作用域内前面要加上“this.”,在单体作用域外前面要加上“circle.”(单体名字.)

 

呵呵,似乎有点味道了!

3.利用分支技术来来封装浏览器之间的差异

注意的地方:

a一定要用闭包,实现即时绑定

b每个分支之间用分号隔开

c最后返回的是分支的名字

d调用的时候用单体名+分支的方法名;

 1  //  利用单体的分支技术来定义XHR(XMLHttpRequest)对象,必须要用闭包才可以实现
 2  var  XHR  =  ( function (){
 3       // The three branches
 4       var  standard  =  {
 5          cXHR: function (){
 6                   return   new  XMLHttpRequest();
 7              }    
 8      };
 9       var  activeXNew  =  {
10          cXHR: function (){
11                   return   new  ActiveXObject( ' Msxml2.XMLHttp ' );
12              }    
13      };
14       var  activeXOld  =  {
15          cXHR: function (){
16                   return   new  ActiveXObject( ' Microsoft.XMLHttp ' );
17              }    
18      };
19       // To assign(分配) the branch, try each method;return whatever doesn't fail
20       var  testObject;
21       try {
22          testObject  =  standard.cXHR();
23           return  standard; //  return this branch if no error was thrown 
24      } catch (e){
25           try {
26              testObject  =  activeXNew.cXHR();
27               return  activeXNew;
28          } catch (e){
29               try {
30                  testObject  =  activeXOld.cXHR();
31                   return  activeXOld;
32              } catch (e){
33                   throw   new  Error( ' Create the XMLHttpRequestObject failed! ' );    
34              }
35          }
36      }
37  })();
38  window.onload  =   function (){
39      alert(XHR.cXHR());
40      }
最后再啰嗦几句:

对于单体据说是最常用的模式之一了,至于利弊嘛要在实践中慢慢的体会了,由于本人也是初学,所以没有太多的发言权,不足指出还忘高手指教















你可能感兴趣的:(JavaScript)