zoukankan      html  css  js  c++  java
  • 《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.');
          }
        }
      }
    
    })();
  • 相关阅读:
    SQL SERVER事务处理
    设计模式之简单工厂模式(静态工厂方法)
    设计模式之工厂方法模式
    为什么静态成员变量要通过类外初始化赋值?
    c++中可以对类中私有成员中的静态变量初始化吗?
    C++中的虚函数(virtual function)
    C++ 基础学习笔记(2)函数(测试题)
    c++中子对象的初始化可在复合类的构造函数的函数体内进行吗?还是子对象的初始化只能在初始化列表中进行?
    C++学习笔记(五)虚函数表解析(转)
    看过的书籍(转)
  • 原文地址:https://www.cnblogs.com/oadaM92/p/4356906.html
Copyright © 2011-2022 走看看