黑铁时代
Programing is not only one kind of technology, but also one kind of art.

  单例模式在传统软件工程中使用非常广泛,单例模式的含义就是如果一个类被设计成单例类,那么从第一次创建它的对象开始,从始至终这个类都只保存一个实例对象,不会有多个类的实力对象。为了保持这个特性,所以通常的做法都是为这个单例类设置一个静态的属性,第一次创建类对象的时候,就将这个类对象保存在这个静态属性中,以后再次实例化类对象的时候,都是取出保存在静态属性中的这个对象,所以从始至终这个类都只会有一个实例对象存在,这就是单例模式。

  JavaScript语言并不像传统面向对象语言那样具有静态变量。但是JavaScript依然可以实现单例模式,而且由于其动态性,会有多种实现方法。

  

  为了能够实现静态变量,我们通常会使用闭包,我们将静态变量保存在闭包中就可以了。

  var singleClass = function() {
    var _instance = null; // 用于保存实例对象的静态变量

    function Class() { // 类的定义

      this.name = 'Leo';
      this.friends = ['Jack'];
    }
    Class.prototype = {
      constructor: Class,
      sayHello: function() {
        alert( 'Hello, ' + this.name );
      }
    }

    return {
      getInstance: function() {
        if ( _instance === null ) { // 如果第一次实例化,就new一个然后保存起来
          _instance = new Class();
        }

        return _instance;
      }
    }
  }();

  var single1 = singleClass.getInstance();
  single1.sayHello(); // 输出'Hello, leo'
  var single2 = singleClass.getInstance();
  single2.name = 'Tommy'; // 设置新的名字
  single2.sayHello(); // 输出'Hello Tommy'
  single1.sayHello(); // 输出'Hello Tommy',single1也跟着改变,说明是同一个实例对象

  这种方式要求大家统一调用getInstance才能取出实例对象,如果习惯使用new操作符去实例化对象的话,应该怎么去控制呢?

  function SingleClass( name ) {    

    var _instance = null; // 保存实例对象的静态变量

    this.name = name;
     _instance = this; // 将this赋值给静态变量
     

    function F() {};
    F.prototype = SingleClass.prototype;

    SingleClass = function () { // 修改构造函数,第一次实例化之后,以后的实例化直接返回保存在静态变量中的实例对象
      return _instance;
    }

    SingleClass.prototype = new F(); // 将原型设置回新的构造函数
  }
  SingleClass.prototype = {
    constructor: SingleClass,
    sayHello: function (){
      alert( 'Hello, ' + this.name );
    }
  }
  var single1 = new SingleClass( 'Leo' );
  single1.sayHello(); // 输出“Hello, Leo”
  single1.name = 'Tommy';
  var single2 = new SingleClass( );
  single2.sayHello(); // 输出“Hello, Tommy”

  使用这种方式的优点就是你不用去关心这个对象是否是单例模式,只需要按照正常的new操作符的方式就可以了。代码中的this在使用new操作符的时候,会指向新的实例对象,所以就相当于将新的实力对象赋值给了静态变量,而新的构造函数作为一个闭包的方式,可以继续使用这个静态变量。注意在修改构造函数的时候,必须使用函数表达式的方式,而不能使用函数申明的方式,因为函数申明会将内层中的SingleClass作为外层中的SingleClass中的一个变量对象,尽管他们具有相同的名字,但他们不再是指向同一个函数对象了。有个问题就是修改构造函数之后,新的构造函数的原型指向了Object,所以我们需要将原来的原型对象设置回新的构造函数,如果这点对项目来说不重要的话,也可以不需要这么做。

 

  其实除了闭包,我们还可以有另外实现静态变量的方式,那就是类变量。JavaScript是一种基于对象的语言,即使是函数,也是Function类型的一个对象实例。如果SingleClass也是一个对象,那么我们就可以为这个对象赋值属性,那么这个属性就可以看成是类变量了。类变量的特点就是不用实例化就可以使用,而且是通过类调用,不是通过对象实例调用,这点更符合面向对象语言的特点。所以将上面的代码修改一下就变成了:

  function SingleClass( name ) {

    if ( null !== SingleClass.Instance ) { // 如果“类变量”不为空,就返回保存在类变量中的实例对象
      return SingleClass.Instance;
    }

    this.name = name;
     SingleClass.Instance = this; // 将实例对象保存在类变量中
  }
  SingleClass.prototype = {
    constructor: SingleClass,
    sayHello: function (){
      alert( 'Hello, ' + this.name );
    }
  }
  SingleClass.Instance = null; // 用于保存对象实例的“类变量”

  关于new操作符的特点,如果没有显示定义返回值,就返回新的实例对象,所以第一次实例化的时候,就会返回this。第二次实例化的时候,会进入if中的代码,那么就会显示返回“类变量”中保存的实例对象了。

posted on 2012-07-22 18:47  黑铁时代  阅读(194)  评论(0编辑  收藏  举报