《javascript设计模式》笔记之第五章:单体模式

一:单体的基本结构

最简单的单体,实际就是一个对象字面量:
var Singleton = {
  attribute1: true,
  attribute2: 10,

  method1: function() {

  },
  method2: function(arg) {

  }
};
二:划分命名空间
单体一个很重要的功能就是划分命名空间,这节其实没什么好说的。。
然后,最好的做法是将命名空间再进一步统一,使自己的所有代码都通过一个全局对象来访问,例子:
var GiantCorp = {};

GiantCorp.Common = {
  // A singleton with common methods used by all objects and modules.
};

GiantCorp.ErrorCodes = {
  // An object literal used to store data.
};

GiantCorp.PageHandler = {
  // A singleton with page specific methods and attributes.
};
然后这样的话,与外部的代码发生冲突的可能性就超级少了
三:用作特定网页专用代码的包装器的单体
单体的另一个重要的功能就是包装网页专用代码:
Namespace.PageName = {

  // Page constants.
  CONSTANT_1: true,
  CONSTANT_2: 10,

  // Page methods.
  method1: function() {

  },
  method2: function() {

  },

  // Initialization method.
  init: function() {

  }
}
然后就调用这些代码
addLoadEvent(Namespace.PageName.init);
接下来举一个实际使用的例子:我们现在写网页基本上必写的就是用ajax提交表单,那么我们怎样用单体来包装这些代码呢?下面就是一个演示:
先来反例(我自己之前的做法~~~):
var formID = document.getElementById("form");
formID.addEventListener('submit',formSubmit,false);

function formSubmit(e) {
        //阻止表单默认动作,然后用ajax发送数据
        //接受数据之后调用callBack函数
}

function callBack(data) {
        //用ajax提交表单之后的回调函数
这样的话污染了超级多的全局变量~~~
然后学习这章之后的做法~~~
var GiantCorp = window.GiantCorp || {};
GiantCorp.RegPage = {
        formID: 'form',
        callBack: function(data) {
                //用ajax提交表单之后的回调函数
        },
        formSubmit: function(e) {
                //阻止表单默认动作,然后用ajax发送数据
                //接受数据之后调用callBack函数
        },
        init: function(e) {
                GiantCorp.RegPage.formEl = document.getElementById(GiantCorp.RegPage.formID);
                GiantCorp.RegPage.formEl.addEventListener('submit',GiantCorp.RegPage.formSubmit,false);
        }
}

GiantCorp.RegPage.init();
注意第一行代码,意思就是如果之前定义过GiantCorp的话就不管,不然就赋予一个空对象给GiantCorp,这样的代码在很多地方都会看到。
四:拥有私用成员的单体
拥有私用成员的最简单方法就是在属性前加下划线_来区分:
GiantCorp.DataParser = {
  // Private methods.
  _stripWhitespace: function(str) {
    return str.replace(/\s+/, '');
  },
  _stringSplit: function(str, delimiter) {
    return str.split(delimiter);
  },
  
  // Public method.
  stringToArray: function(str, delimiter, stripWS) {
    if(stripWS) {
      str = this._stripWhitespace(str);
    }
    var outputArray = this._stringSplit(str, delimiter);
    return outputArray;
  }
};
上面的代码不用去理解它,看到下划线_就把它当成是私有属性就可以了
此外stringToArray的函数里面用了this来直接调用单体里面的其他方法,不过使用起来注意它有时候指向的对象的问题!
接下来是使用闭包来实现真正拥有私用成员的单体:
MyNamespace.Singleton = (function() {
  // Private members.
  var privateAttribute1 = false;
  var privateAttribute2 = [1, 2, 3];
  
  function privateMethod1() {
    ...
  }
  function privateMethod2(args) {
    ...
  }

  return { // Public members.
    publicAttribute1: true,
    publicAttribute2: 10,
    
    publicMethod1: function() {
      ...
    },
    publicMethod2: function(args) {
      ...
    }
  };
})();
注意是公共成员都是放在return里面的,其他都是私有的。
这种模式是JavaScript中最流行,应用最广泛的模式之一
五:惰性实例化
又来一个比较高级的做法,那就是惰性实例化:当我们需要用到这段代码的时候才创建它。换种方法说就是这样调用其方法:Singleton.getInstance().methodName(),而不是这样调用:Singleton.methodName()。其中getInstance方法会检查该单体是否已经被实例化。如果还没有,那么它将创建并返回其实例,如果单体已经实例过,那么它将返回现有实例。
 
实现步骤一(单体的所有代码移动一个名为constructor的方法中):
MyNamespace.Singleton = (function() {

  function constructor() { // All of the normal singleton code goes here.
    // Private members.
    var privateAttribute1 = false;
    var privateAttribute2 = [1, 2, 3];
  
    function privateMethod1() {
      ...
    }
    function privateMethod2(args) {
      ...
    }

    return { // Public members.
      publicAttribute1: true,
      publicAttribute2: 10,
    
      publicMethod1: function() {
        ...
      },
      publicMethod2: function(args) {
        ...
      }
    }
  }
  
})();
上面注意constructor是包含单体里面所有代码的。
实现步骤二(创建一个单体控制的函数,并返回到单体):
MyNamespace.Singleton = (function() {
  
  function constructor() { // All of the normal singleton code goes here.
    ...
  }
  
  return {
    getInstance: function() {
      // Control code goes here.
    }
  }
})();
实现步骤三(创建个私有变量用于判断是否已经实例化,并且在getInstance实现判断逻辑):
MyNamespace.Singleton = (function() {
  
  var uniqueInstance; // Private attribute that holds the single instance.
  
  function constructor() { // All of the normal singleton code goes here.
    ...
  }
  
  return {
    getInstance: function() {
      if(!uniqueInstance) { // Instantiate only if the instance doesn't exist.
        uniqueInstance = constructor();
      }
      return uniqueInstance;
    }
  }
})();
此外,可以用 var MNS = MyNamespace.Singleton的方法简化命名空间,但是要注意this指向问题!
六:分支
利用分支可以实现判断浏览器支持哪一种代码,然后对该浏览器使用专用的代码:
MyNamespace.Singleton = (function() {
  var objectA = {
    method1: function() {
      ...
    },
    method2: function() {
      ...
    }
  };
  var objectB = {
    method1: function() {
      ...
    },
    method2: function() {
      ...
    }
  };

  return (someCondition) ? objectA : objectB;
})();
用上面这样的分支我们就可以用来实现如果是IE8就返回第一种单体,如果不是就返回第二种单体了。
七:示例:用分支技术创建XHR对象
如果要兼容老旧的浏览器使用ajax,那就可以用这种技术了
例子:
/* SimpleXhrFactory singleton, step 1. */

var SimpleXhrFactory = (function() {
  
  // The three branches.
  var standard = {
    createXhrObject: function() {
      return new XMLHttpRequest();
    }
  };
  var activeXNew = {
    createXhrObject: function() {
      return new ActiveXObject('Msxml2.XMLHTTP');
    }
  };
  var activeXOld = {
    createXhrObject: function() {
      return new ActiveXObject('Microsoft.XMLHTTP');
    }
  };
  
})();

/* SimpleXhrFactory singleton, step 2. */

var SimpleXhrFactory = (function() {
  
  // The three branches.
  var standard = {
    createXhrObject: function() {
      return new XMLHttpRequest();
    }
  };
  var activeXNew = {
    createXhrObject: function() {
      return new ActiveXObject('Msxml2.XMLHTTP');
    }
  };
  var activeXOld = {
    createXhrObject: function() {
      return new ActiveXObject('Microsoft.XMLHTTP');
    }
  };
  
  // To assign the branch, try each method; return whatever doesn't fail.
  var testObject;
  try {
    testObject = standard.createXhrObject();
    return standard; // Return this if no error was thrown.
  }
  catch(e) {
    try {
      testObject = activeXNew.createXhrObject();
      return activeXNew; // Return this if no error was thrown.
    }
    catch(e) {
      try {
        testObject = activeXOld.createXhrObject();
        return activeXOld; // Return this if no error was thrown.
      }
      catch(e) {
        throw new Error('No XHR object found in this environment.');
      }
    }
  }

})();
posted @ 2015-03-22 11:19  oadaM92  阅读(222)  评论(0编辑  收藏  举报