JS设计模式一:单例模式

单例模式也称作为单子模式,更多的也叫做单体模式。为软件设计中较为简单但是最为常用的一种设计模式。
 
    下面是维基百科对单例模式的介绍:
    在应用单例模式时,生成单例的类必须保证只有一个实例的存在,很多时候整个系统只需要拥有一个全局对象,才有利于协调系统整体的行为。比如在整个系统的配置文件中,配置数据有一个单例对象进行统一读取和修改,其他对象需要配置数据的时候也统一通过该单例对象来获取配置数据,这样就可以简化复杂环境下的配置管理。

    单例模式的思路是:一个类能返回一个对象的引用(并且永远是同一个)和一个获得该实例的方法(静态方法,通常使用 getInstance 名称)。那么当我们调用这个方法时,如果类持有的引用不为空就返回该引用,否者就创建该类的实例,并且将实例引用赋值给该类保持的那个引用再返回。同时将该类的构造函数定义为私有方法,避免其他函数使用该构造函数来实例化对象,只通过该类的静态方法来得到该类的唯一实例。
 
    对于 JS 来说,巨大的灵活性使得其可以有多种方式实现单例模式,使用闭包方式来模拟私有数据,按照其思路可得:

var single = (function(){

    var unique;

    function getInstance(){

        if( unique === undefined ){

            unique = new Construct();

        }

        return unique;

    }

    function Construct(){

        // ... 生成单例的构造函数的代码

    }

    return {

        getInstance : getInstance

    }

})();

    以上,unique便是返回对象的引用,而 getInstance便是静态方法获得实例。Construct 便是创建实例的构造函数。
    可以通过 single.getInstance() 来获取到单例,并且每次调用均获取到同一个单例。这就是 单例模式 所实现的效果。
 
    不过,对于JS来说,显然以上循规蹈矩的方式显得过于笨重,在不同的场景以不同的方式实现单体模式正是 JS 的优势
    

    实现1: 最简单的对象字面量

var singleton = {

        attr : 1,

        method : function(){ return this.attr; }

    }

var t1 = singleton ;

var t2 = singleton ;

    那么很显然的, t1 === t2 。
 
    十分简单,并且非常使用,不足之处在于没有什么封装性,所有的属性方法都是暴露的。对于一些需要使用私有变量的情况就显得心有余而力不足了。当然在对于 this 的问题上也是有一定弊端的。
 

    实现2:构造函数内部判断

    其实和最初的JS实现有点类似,不过是将对是否已经存在该类的实例的判断放入构造函数内部。

function Construct(){

    // 确保只有单例

    if( Construct.unique !== undefined ){

        return Construct.unique; 

    }

    // 其他代码

    this.name = "NYF";

    this.age="24";

    Construct.unique = this;

}

var t1 = new Construct() ;

var t2 = new Construct() ;

    那么也有的, t1 === t2 。
    也是非常简单,无非就是提出一个属性来做判断,但是该方式也没有安全性,一旦我在外部修改了Construct的unique属性,那么单例模式也就被破坏了。
    

    实现3 : 闭包方式    

    对于大着 灵活 牌子的JS来说,任何问题都能找到 n 种答案,只不过让我自己去掂量孰优孰劣而已,下面就简单的举几个使用闭包实现单例模式的方法,无非也就是将创建了的单例缓存而已。

var single = (function(){

    var unique;

    function Construct(){

        // ... 生成单例的构造函数的代码

    }

    unique = new Constuct();

    return unique;

})();

    只要 每次讲 var t1 = single; var t2 = single;即可。 与对象字面量方式类似。不过相对而言更安全一点,当然也不是绝对安全。
    如果希望会用调用 single() 方式来使用,那么也只需要将内部的 return 改为
    
    return function(){
        return unique;
    } 
    以上方式也可以使用 new 的方式来进行(形式主义的赶脚)。当然这边只是给了闭包的一种例子而已,也可以在 Construct 中判断单例是否存在 等等。 各种方式在各个不同情况做好选着即可。
 

总结

    总的来说,单例模式相对而言是各大模式中较为简单的,但是单例模式也是较为常用并且很有用的模式。在JS中尤为突出(每个对象字面量都可以看做是一个单例么~)。
    记住,是否严格的只需要一个实例对象的类(虽然JS没有类的概念),那么就要考虑使用单例模式。
    使用数据缓存来存储该单例,用作判断单例是否已经生成,是单例模式主要的实现思路。


以下为单例模式实现的 点击规则 效果

/* 查看规则*/
// ************单例模式 **********//
var RuleDialog = (function(){
  var cDialog = function(fn){
    var str = "<div class='ruleDialog' id='ruleDialog'>" +
          "<h4 style='text-align:center;font-size:1rem;'>详细活动规则</h4>" +
          "<a href='javascript:' class='closeRuleDialog' id='closeRuleBtn'>&times;</a>" +
          "<div>1、本活动奖品全部由合作伙伴国鑫所提供。<br/>" +
              "2、电影票(蜘蛛网兑换码)在微信关注“国鑫所微服务”后一个工作日内以短信形式将兑换验证码发送到所填手机号。<br/>" +
              "3、鑫币在微信关注“国鑫所微服务”后一个工作日内自动发放到国鑫所平台账户中。鑫币有效期为30天,请注意及时使用。<br/>" +
              "4、实物奖励在微信关注“国鑫所微服务”后联系工作人员后发放。<br/>" +
              "5、本次抽奖仅限国鑫所未注册用户参加。<br/>" +
              "活动最终解释权归国鑫所所有,如有问题请联系在线客服或拨打400-885-2506" +
          "</div>" +
       "</div>";
    var ruleWin = null;
    return function(){
      var type = [].shift.call(arguments);
      if(type === "show"){
        if(!ruleWin) ruleWin = fn(str);
        else ruleWin.show();
      }
      else if(type === "hide") ruleWin.hide();
    }
  };

  return cDialog(function(str){
    var dialogWin = $("<div class='dialogWin overlayer' id='ruleDialog'></div>").appendTo("body").html(str);
    $("#closeRuleBtn").click(function(){RuleDialog("hide")});
    return dialogWin
  });
})();

$("#showRuleBtn").click(function(){RuleDialog("show")});

posted @ 2017-03-21 16:25  a fine day  阅读(133)  评论(0)    收藏  举报