JS设计模式—单体模式(即时加载/惰性加载)

单体模式作为一种软件开发模式在众多面向对象语言中得到了广泛的使用,在javascript中,单体模式也是使用非常广泛的,但是由于javascript语言拥有其独特的面向对象方式,导致其和一些传统面向对象语言虽然在单体模式的思想上是一致的,但是实现起来还是有差异的。

首先来看看传统面向对象语言对于单体模式的定义:单体模式是只能被实例化一次并且可以通过一个众所周知的访问点来访问的类。这个定义有两点突出了传统面向对象语言的特征,即类和实例化,所以对于传统面向对象语言来讲,单体模式是建立在其类和实例化的自然特性之上的,即使用关键字class定义一个类,该类可通过new关键字来实例化,但是需要保证每次被new实例化之后得到的都是同一个实例或者说只能通过new来调用其构造函数一次。

再来看看javascript中对于单体模式的定义:单体是一个用来划分命名空间并将一批相关方法和属性组织在一起的对象,如果它能够被实例化,那么只能被实例化一次。对比上面的定义,你会发现这里的单体定义将其实质定义为对象,而不是传统面向对象语言中的类,这也表明了javascript这门语言是基于对象的。同时后面又指出,如果能够被实例化,这说明了在javascript中单体定义应该有好几种方式,存在一种或几种能够被实例化即使用new关键字来创建单体对象的方式,但是这种方式不是javascript自身的自然特征,因为使用new关键字创造出来的对象,实际上都是通过function来模拟定义其构造函数的(虽然ES6开始支持class关键字了,但是目前还没有得到浏览器广泛支持),那么如何使用javascript的自然特征来实现单体模式呢?

1  var Singleton={
2       attribute1:true,
3       attribute2:10,
4       method1:function(){
5       },
6       method2:function(arg){
7       }
8   }

这里定义了一个对象Singleton,内部包含若干属性和方法,将其包含在页面中,js载入的时候就创建了这个对象,在调用时使用Singleton.method1来调用,它的实例化是随着页面载入js解析执行过程中完成的,我们并没有使用new关键字来实例化这个对象,这也是javascript中实现单体模式和传统面向对象语言一个很大的不同。这种方式更为简单易于理解。但是这种方式存在若干缺点,一个很明显的缺点是它并没有提供命名空间,其他程序员如果在页面中也定义了一个Singleton变量,那么很容易改写和混淆这个单体对象,于是针对这个问题,将其改写如下:

 1   var mySpace={};
 2   mySpace.Singleton={
 3       attribute1:true,
 4       attribute2:10,
 5       method1:function(){
 6
 7      },
 8       method2:function(arg){
 9
10      }
11  } 

这里首先定义了一个mySpace的命名空间,然后将单体对象Singleton挂载在这个对象的下面,这大大减少了和其他程序员冲突以及误操作的可能,即使其他人在全局作用域中定义一个Singleton变量,也不会污染到这个单体对象,这就实现了前面定义中所说的划分命名空间并且将一些相关属性和方法组织在一起的功能。

这个方法依然存在缺点,这个单体对象的所有属性和方法都是共有的,外部可随时访问和修改,于是采用闭包来模拟私有属性和方法,如下:

 1 mySpace.Singleton=(function(){
 2      var privateAttribute1=false;
 3      var privateAttribute1=[1,2,3];
 4      function privateMethod1(){
 5
 6       }
 7      function privateMethod2(){
 8
 9      }
10
11     return {
12      publicAttribute1:true,
13      publicAttribute2:10,
14      publicMethod1:function(){
15          privateAttribute1=true;
16          privateMethod1();
17      },
18      publicMethod2:function(arg){
19          privateAttribute1=[4,5,6];
20          privateMethod2();
21      }
22
23     }
24
25  })();

在这里我们直接给该单体对象赋值了一个匿名自执行的函数,在该函数中使用var和function关键字分别来定义其私有属性和方法,这些在函数外部(单体对象外部)是无法直接访问的,因为函数一执行完毕,其内部作用域的空间就会被回收,这也就是能够利用闭包来模拟私有属性和方法的原因所在。在该函数(闭包)中,同时最终返回一个对象,这个对象中包含一些公有方法和属性,在外部可以直接调用,同时这些公有方法由于定义在函数内部,所以可以调用其私有属性和方法,但是外界只能通过返回的公有方法和属性来完成某些操作,不能够直接调用Singleton.privateMethod1这些属性。这就使得该单体对象既隔离了外界去直接访问其私有属性和方法,又提供给外界一些共有属性和方法去完成某些操作。

这种匿名函数自执行所构造的单体模式在很多js库中被广泛使用,但是依然存在一个问题,如果我们在载入页面的时候并不需要用到该对象,而且该对象的创建比较耗费开销(如需要进行大量计算或需要多次访问dom树及其属性等)时,合理的做法是需要它的时候再去创建它,而不是随着js的解析执行直接去创建,这种概念被称之为惰性加载(lazy loading),于是修改以上代码如下:

 1    mySpace.Singleton=(function(){
 2           var uniqueInstance;  //匿名函数创建私有变量,判断单体对象是否被创建的句柄
 3           function constructor(){
 4               var privateAttribute1=false;
 5               var privateAttribute1=[1,2,3];
 6              function privateMethod1(){
 7                }
 8               function privateMethod2(){
 9                }
10               return {
11                   publicAttribute1:true,
12                   publicAttribute2:10,
13                   publicMethod1:function(){
14                       privateAttribute1=true;
15                      privateMethod1();
16                   },
17                   publicMethod2:function(arg){
18                      privateAttribute1=[4,5,6];
19                       privateMethod2();
20                   }
21
22               }
23           }
24           return {
25               getInstance:function(){
26                if(!uniqueInstance){
27                    uniqueInstance=constructor();
28                }
29                  return uniqueInstance;
30               }
31           }
32    })();

这里首先在匿名函数中定义了一个私有变量uniqueInstance,作为一个判断单体对象是否被创建出来的句柄,然后将刚才所有对单体对象定义的属性和方法都放在一个名为constructor的函数中,只有该函数调用了,才会创造出该单体对象,否则不会直接创建它。然后,返回一个对象,其包含一个getInstance方法,该方法是供外部调用的,调用该方法的时候首先判断该单体对象是否存在,如果存在就直接返回它,否则调用constructor函数构造这个单体对象再返回它。最后如果我们调用该单体对象的某个方法,需要使用mySpace.Singleton.getInstance().publicMethod1(),这里,只有我们这样调用的时候才会创建这个单体对象,否则该单体对象是不会被自动创建的,这实际上就实现了按需加载或者惰性加载。

时间: 2024-11-08 08:40:59

JS设计模式—单体模式(即时加载/惰性加载)的相关文章

如何做JS 单体模式的设计---->>js设计模式<<-------单体模式

1. 单体模式是js中最基本 单最有用的模式之一,非常常用. 单体模式的基本结构如下: var Person = { name: 'lilu', age:'20', sayHi: function(){ alert('456456'); }, sayBye: function(){ alert('456456'); } } 这个栗子中,所有的成员都可以通过Person进行访问,用点运算符就行. 如: Person.name = "zczc"; Person.age = "10

js设计模式--单体模式

单体模式 -单体模式(singleton)是javaScript中最基本最有用的设计模式之一. -这种模式提供了一种将代码组织为一个逻辑单元的手段,这个逻辑单元中的代码可以通过单一 的变量进行访问.通过确保单体对象只存在一份实例,你就可以确信自己的所有代码使用的都是 同样的全局资源. -简单单体 -闭包单体 -惰性单体 -分支单体 <script type="text/javascript" charset="utf-8"> // 单体模式(single

5分钟读书笔记之 - 设计模式 - 单体模式

单体是一个用来划分命名空间,并将一批相关方法和属性组织在一起的对象,如果它可以被实例化,那么它只能被实例化一次. 单体模式,就是将代码组织为一个逻辑单元,这个逻辑单元中的代码可以通过单一的变量进行访问. 单体基本结构是这样: var Singleton = { attribute1:true, attribute2:10, method1:function(){}, method2:function(){} } 借助闭包实现单体: Namespace.Singleton = {} 定义之后立即执

js设计模式——桥接模式

定义:将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化. 常用场景:在js中,桥接模式常用于事件监听器和ajax请求的解耦,以便于进行单元测试. 举个栗子 普通方法. var btn=$('#btn'); btn.on('click',function () { $.ajax({ url:'test.html', data:{ id:this.id }, dataType:'html', success:function(data){ con

JavaScript设计模式——单体模式

一:单体模式简介: 是什么:将代码组织为一个逻辑单元,这个单元中的代码通过单一的变量进行访问.只要单体对象存在一份实例,就可以确信自己的所有代码使用的是同样的全局资源. 用途:1.用来划分命名空间,减少网页中全局变量的数目. 2.在分支技术中用来封装浏览器之间的差异. 3.单体对象创建的命名空间可以快速清除全局变量. 4.增强模块性 关于单体模式的好坏,等你看完所有的讲解之后再告诉你哦....... 二:单体结构: 1.最简单的单体就是一个对象字面量. 1 var ProductTools={

js设计模式-命令模式

命令模式是一种组织型模式,主要用在把调用对象(用户界面.API和代理等)与实现操作的对象隔离开.也就是说 ,凡是两个对象间的互动方式需要更高的模块化程度时都可以用到这种模式. 命令模式的好处:1.提高程序的模块化程度和灵活性.(运用得当):2.实现取消和状态恢复等复杂的有用特性非常容易. 例子:菜单组合对象 1 /** 2 * 菜单组合对象 3 */ 4 5 /*command,Composite and MenuObject interfaces*/ 6 var Command = new I

JS设计模式——工厂模式详解

工厂模式是另外一种关注对象创建概念的创建模式.它的领域中同其它模式的不同之处在于它并没有明确要求我们使用一个构造器.取而代之,一个工厂能提供一个创建对象的公共接口,我们可以在其中指定我们希望被创建的工厂对象的类型. 简单工厂模式:使用一个类(通常为单体)来生成实例 复杂工厂模式:使用子类来决定一个变量成员应该为哪个具体的类的实例. 简单工厂模式 var BicycleShop = function () { }; BicycleShop.prototype = { sellBicycle: fu

js设计模式-桥接模式

桥接模式定义:桥梁模式的用意是"将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化".这句话有三个关键词,也就是抽象化.实现化和脱耦. 最简单的桥接模式例子:事件监听器 addEvent(element,"click",getResultByIdBridge); function getResultByIdBridge(e){ getById(this.id, function(result){ //TODO: t

js设计模式-组合模式

组合模式是一种专为创建web上的动态用户界面而量身定制的模式.使用这种模式,可以用一条命令在多个对象上激发复杂的或递归的行为.这可以简化粘合性代码,使其更容易维护,而那些复杂行为则被委托给各个对象. 组合模式实例:图片库 1 /** 2 * 图片库 3 */ 4 var Composite = new Interface("Composite",["add","remove","getChild"]); 5 var Galle