JavaScript中的单例模式

单例模式javascript

在JavaScript中,单例(Singleton)模式是最基本又最有用的模式之一。这种模式提供了一种将代码组织为一个逻辑单元的手段,这个逻辑单元中的代码能够经过单一的变量进行访问。确保单例对象只有一份实例,你就能够确信本身的全部代码使用的都是一样的全局资源。html

单例类在JavaScript中用途普遍:java

(1)能够用来划分命名空间,以减小网页中全局变量的数量;程序员

(2)能够在一种名为分支的技术中用来封装浏览器之间的差别;数组

(3)能够借助于单例模式,将代码组织得更为一致,从而使代码更容易阅读和维护。浏览器

 单例的基本结构安全

最基本的单例其实是一个对象字面值,它将一批有必定关联的方法和属性组织在一块儿。例如以下JavaScript代码:闭包

var Singleton = {
    attribute1: true;
    attribute2: 10
    
    method1: function() { },
    method2: function() { }   
 };

这些成员能够经过Singleton加圆点运算符来访问:异步

Singleton.attribute1 = false;
 var total = Singleton. attribute2 + 5;
 var result = Singleton.method1();

 

对象字面值只是用以建立单例的方法之一,后面介绍的那些方法所建立的单体看起来更像其余面向对象语言中的单例类。另外,并不是全部对象字面值都是单体,若是它只是用来模仿关联数组或容纳数据的话,那显然不是单例。但若是它是用来组织一批相关方法和属性的话,那就多是单例,其区别主要在于设计者的意图。函数

建立拥有私有成员的单例
  使用下划线表示法
  在单例对象内建立类的私有成员的最简单、最直截了当的方法是使用下划线表示法(在JavaScript业界,若是变量和方法是使用下划线,则表示该变量和方法是私有方法,只容许内部调用,第三方不该该去调用)。参考实例以下:

GiantCorp.DataParser = {
    // 私有方法
    _stripWhitespace: function(str) {
        return str.replace(/\s+/, ''); 
    },
    _stringSplit: function(str, delimiter) {
        return str.split(delimiter);
    },
    // 公用方法
    stringToArray: function(str, delimiter, stripWS) {
        if (stripWS) {
            str = this._stripWhitespace(str);
        }
        
        var outputArray = this._stringSplit(str, delimiter);
        return outputArray;
    }
};

  

  在如上代码中,stringToArray方法中使用this访问单体中的其余方法,这是访问单体中其余成员或方法的最简便的方法。但这样作有一点风险,由于this并不必定指向GiantCorp.DataParser例如,若是把某个方法用做事件监听器,那么其中的this指向的是window对象,所以大多数JavaScript库都会为事件关联进行做用域校订,例如在这里使用GiantCorp.DataParser比使用this更为安全。

使用闭包
  在单例对象中建立私有成员的第二种方法是借助闭包。由于单例只会被实例化一次,因此没必要担忧本身在构造函数中声明了多少成员。每一个方法和属性都只会被建立一次,因此能够把它们声明在构造函数内部(所以也就位于同一个闭包中)。

使用闭包建立拥有私有成员的单例类的实例以下:

MyNamespace.Ssingleton = (function() {
    // 私有成员
     var privateAttribute1 = false;
     var privateAttribute2 = [1, 2, 3];
 
    function privateMethod1() { }    
    function privateMethod2() { }
 
    return {
        // 公有成员
        publicAttribute1: true;
        publicAttribute2: 10,
        
        publicMethod1: function() { },
        publicMethod2: function() { }
    };
 })();

  

这种单例模式又称为模块模式,指的是它能够把一批相关方法和属性组织为模块并起到划分命名空间的做用。 使用该种方式改造上面的实例,参考代码以下:

 GiantCorp.DataParser = (function() {
    var whiteSpaceRegex = /\s+/;
    // 私有方法
    function stripWhitespace(str) {
        return str.replace(whiteSpaceRegex, ''); 
    }
    function stringSplit(str, delimiter) {
        return str.split(delimiter);
    },
    return {
      // 公用方法
      stringToArray: function(str, delimiter, stripWS) {
        if (stripWS) {
           str = stripWhitespace(str);
        }
            
      var outputArray = stringSplit(str, delimiter);
         return outputArray;
      }
    };
 })();

  将私有成员放在闭包中能够确保其不会在单例对象以外被使用,所以开发人员能够自由的改变对象的实现细节,而不会殃及别人的代码。还可使用这种办法对数据进行保护和封装。

在JavaScript中实现“懒汉式”单例模式
  在如上的代码中,单例对象都是在脚本加载时被建立出来。对于资源密集型或配置开销甚大的单例,更合理的是使用“懒汉式”单例实现。这种实现方式的特别之处在于,对它们的访问必须借助于一个静态方法,例如调用单例类的getInstance()方法得到对象实例。

参考实现代码以下:

MyNamespace.Singleton = (function() {
    // 定义一个私有的属性,该属性用于储存单例对象
  var uniqueInstance;
  function constructor() {
    // 将单态操做放在这里
  }
  return {
    getInstance: function() {
       if (!uniqueInstance) {
           uniqueInstance = constructor();
        }
           
        return uniqueInstance;
     }
  }
 })();

将一个单例转换为懒汉式加载方式后,必须对调用它的代码进行修改,例如以前调用:

MyNamespace.Singleton.publicMethod1();

应该改为以下代码:

MyNamespace.Singleton.getInstance().publicMethod1(); 

若是以为命名空间名称太长,能够建立一个别名来简化它。

使用单例模式实现分支

  分支是一种用来把浏览器之间的差别封装到运行期间进行设置的动态方法中的技术。例如,假设咱们须要一个建立XHR对象的方法,这种XHR对象在大多数浏览器中是XMLHttpRequest对象的实例,但在IE早期版本中是某种ActiveX类的实例,这样一种方法一般会进行某种浏览器嗅探或对象探测。若是不用分支技术,那么每次调用这个方法时,全部这些浏览器嗅探代码都要再次运行。若是该方法调用频繁,将会严重影响效率。

要实现获取不一样浏览器的XHR对象的功能,参考实现代码的实现步骤以下:

(1)判断有多少个分支(有3个),这些分支按其返回的XHR对象类型命名,这三个分支都有一个名为createXhrObject()的方法,该方法返回一个能够执行异步请求的新对象;

(2)根据条件将3个分支中某一分支的对象赋给那个变量,具体作法是逐一尝试XHR对象,直到遇到一个当前JavaScript环境所支持的对象为止。

参考代码以下所示:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
 <head>
 <title>使用单例模式实现JavaScript中的分支</title>
 <script type="text/javascript">

 var SimpleXhrFactory = (function() {
 // 三个分支
 var standard = {
  createXhrObject: function() {
    alert("standard createXhrObject");
    return new XMLHttpRequest();
  }
 };
 var activeXNew = {
   createXhrObject: function() {
     alert("activeXNew createXhrObject");
     return new ActiveXObject("Msxml2.XMLHTTP");
   }
 };
 var activeXOld = {
   createXhrObject: function() {
     alert("activeXOld createXhrObject");
     return new ActiveXObject("Microsoft.XMLHTTP");
   }
  };
  // 为分支赋值
  var testObject;
   try {
       testObject = standard.createXhrObject();
       return standard;
   } catch(e) {
     try {
      testObject = activeXNew.createXhrObject();
      return activeXNew;
     } catch(e) {
     try {
        testObject = activeXOld.createXhrObject();
        return activeXOld;
     } catch(e) {
        throw new Error("在该浏览器环境中没有发现XHR对象.");
     }
   }
  } 
 })();
 SimpleXhrFactory.createXhrObject();
 

</script></head><body></body> 

  用了分支技术后,全部的那些特性嗅探代码都只会执行一次,而不是没生成一个对象执行一次。这种技术适用于任何只有在运行时才能肯定具体实现的状况。

单例模式的优缺点
  在JavaScript中使用单例模式的主要优势以下:

(1)对代码的组织做用:它将相关方法和属性组织在一个不会被屡次实例话的单例中,可使代码的调试和维护变得更轻松。描述性的命名空间还能够加强代码的自我说明性。将方法包裹在单例中,能够防止它们被其它程序员误改。

(2)单例模式的一些高级变体能够在开发周期的后期用于对脚本进行优化。

主要缺点以下:

(1)由于提供的是一种单点访问,因此它有可能致使模块间的强耦合。单体最好是留给定义命名空间和实现分支型方法这些用途,这些状况下,耦合不是什么问题;

(2)有时某些更高级的模式会更符合任务的须要。与“懒汉式”加载单例相比,虚拟代理能给予你对实例化方式更多的控制权。也能够用一个真正的对象工厂来取代分支型单例。

 

本文学习地址:http://www.108js.com/article/article5/50021.html?id=333

相关文章
相关标签/搜索