学习JavaScript设计模式(单例模式)


Posted in Javascript onNovember 26, 2015

单例模式的定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏览器的window对象。在js开发中,单例模式的用途同样非常广泛。试想一下,当我们单击登录按钮的时候,页面中会出现一个登录框,而这个浮窗是唯一的,无论单击多少次登录按钮,这个浮窗只会被创建一次。因此这个登录浮窗就适合用单例模式。

1、单例模式的使用场景

在使用一种模式之前,我们最好要知道,这种模式的使用场景。用了这么久的单例模式,竟全然不知!用它具体有哪些好处呢?

1).可以用它来划分命名空间(这个就是就是经常用的了)

2).利用分支技术来封装浏览器之间的差异(这个还真没用过,挺新鲜)

3).借助单例模式,可以把代码组织的更为一致,方便阅读与维护(这个也用过了)

2、最基本的单例模式

最简单的单例其实就是一个对象字面量。它把一批有一定关联的方法和属性组织在一起。

var Singleton = {
  attr1: true , 
  attr2: 10 ,
  method1 : function(){
    alert('我是方法1');
  },
  method2 : function(){
    alert('我是方法2');
  }
};

这个对象可以被修改。你可以添加属性和方法。你也可以用delete运算符删除现有成员。这实际上违背了面向对象设计的一条原则:类可以被扩展,但不应该被修改。如果某些变量需要保护,那么可以将其定义在闭包中。

对象字面量只是创建单例的方法之一。也并非所有的对象字面量都是单例,那些只是用来模仿关联数组或容纳数据的对象字面量显然不是单例。

3、借用闭包创建单例

闭包主要的目地 保护数据

// 命名空间
var BHX = {} ;
BHX.Singleton = (function(){
  // 添加自己的私有成员
  var a1 = true ;
  var a2 = 10 ;
  var f1 = function(){
    alert('f1');
  }
  var f2 = function(){
    alert('f2');
  }        
  // 把块级作用域里的执行结果赋值给我的单例对象
  return {
      attr1: a1 , 
      attr2: a2 ,
      method1 : function(){
        return f1();
      },
      method2 : function(){
        return f2();
      }            
  } ;
})();

alert(BHX.Singleton.attr1);
BHX.Singleton.method1();

这种单例模式又称模块模式,指的是它可以把一批相关的方法和属性组织为模块并起到划分命名空间的作用。

4、单例模式用于划分命名空间

1)、防止全局声明的修改

/*using a namespace*/

var BHX = {};
BHX.Singleton = {
  attr1: true , 
  attr2: 10 ,
  method1 : function(){
    alert('我是方法1');
  },
  method2 : function(){
    alert('我是方法2');
  }        
};
BHX.Singleton.attr1;
var attr1 = false;

这样以来,即使我们在外面声明了相同的变量,也能在一定程度上防止attr1的被修改。

2)、防止其它来源代码的修改

现在网页上的JavaScript代码往往不止用一个来源,什么库代码、广告代码和徽章代码。为了避免与自己代码的冲突,可以定义一个包含自己所有代码的对象。

var XGP = {};
XGP.Common = {
  //A singleton with common methods used by all objects and modules
}
XGP.ErrorCodes = {
  //An object literal used to store data
}
XGP.PageHandler = {
  //A singleton with page specific methods and attributes.
}

3)、用作专用代码封装

在拥有许多网页的网站中,有些代码是所有网页都要用到的,他们通常被存放在独立的文件中;而有些代码则是某个网页专用的,不会被用到其他地方。最好把这两种代码分别包装在自己的单例对象中。

我们经常要用Javascript为表单添加功能。出于平稳退化方面的考虑,通常先创建一个不依赖于Javascript的、使用普通提交机制完成任务的纯HTML网页。

XGP.RegPage = {
  FORM_ID: 'reg-form',
  OUTPUT_ID: 'reg-result',

  handleSubmit: function(e){
    e.preventDefault(); //stop the normal form submission

    var data = {};
    var inputs = XGP.RegPage.formEl.getElementByTagName('input');

    for(var i=0, len=inputs.length; i<len; i++){
      data[inputs[i].name] = inputs[i].value;
    }

    XGP.RegPage.sendRegistration(data);
  },
  sendRegistration: function(data){
    //make an xhr request and call displayResult() when response is recieved
    ...
  },
  displayResult: function(response){
    XGP.RegPage.outputEl.innerHTML = response;
  },
  init: function(){
    XGP.RegPage.formEl =$(XGP.RegPage.Form_ID);
    XGP.RegPage.outputEl = $(XGP.RegPage.OUTPUT_ID);
    //hijack the form submission
    addEvent(XGP.RegPage.formEl, 'submit', XGP.RegPage.handleSubmit);
  }
}
//invoke initialization method after the page load
addLoadEvent(XGP.RegPage.init);

5、惰性单例

前面所讲的单例模式又一个共同点:单例对象都是在脚本加载时被创建出来。对于资源密集的或配置开销甚大的单例,更合理的做法是将其实例化推迟到需要使用他的时候。

这种技术就是惰性加载(lazy loading)。

实现步骤如下:

1).将所有代码移到constructor方法中

2).全权控制调用时机(正是getInstance所要做的)

XGP.lazyLoading = (function(){
  var uniqInstance;

  function constructor(){
    var attr = false;
    function method(){

    }

    return {
      attrp: true,
      methodp: function(){

      }
    }
  }

  return {
    getInstance: function(){
      if(!uniqInstance){
        uniqInstance = constructor();
      }
      return uniqInstance;
    }
  }
})();

6、分支技术

分支是一种用来把浏览器间的差异封装在运行期间进行设置的动态方法中的技术。

// 分支单例 (判断程序的分支 <浏览器差异的检测>)
var Ext = {} ;
var def = false ;
Ext.More = (function(){
  var objA = {    // 火狐浏览器 内部的一些配置
      attr1:'FF属性1'
      // 属性1 
      // 属性2 
      // 方法1 
      // 方法2
  } ;
  var objB = {    // IE浏览器 内部的一些配置
      attr1:'IE属性1'
      // 属性1 
      // 属性2 
      // 方法1 
      // 方法2             
  } ;
  return (def) ?objA:objB;
})();
alert(Ext.More.attr1);

比如说,如果网站中要频繁使用xhr,每次调用都要再次运行浏览器嗅探代码,这样会严重缺乏效率。更有效的做法是在脚本加载时一次性地确定针对浏览器的代码。这正是分支技术所做的事情。当然,分支技术并不总是更高效的选择,在两个或者多个分支中只有一个分支被用到了,其他分支就占用了内存。

在考虑是否使用分支技术的时候,必须在缩短时间和占用更多内存这一利一弊之间权衡一下。

下面利用分支技术实现XHR:

var XHR = (function(){
  var standard = {
    createXhrObj: function(){
      return new XMLHttpRequest();
    }
  };
  var activeXNew = {
    createXhrObj: function(){
      return new ActiveXObject('Msxml2.XMLHTTP');
    }
  };
  var activeXOld = {
    createXhrObj: function(){
      return new ActiveXObject('Microsoft.XMLHTTP');
    }
  };

  var testObj;
  try{
    testObj = standard.createXhrObj();
    return testObj;
  }catch(e){
    try{
      testObj = activeXNew.createXhrObj();
      return testObj;
    }catch(e){
      try{
        testObj = activeXOld.createXhrObj();
        return testObj;
      }catch(e){
        throw new Error('No XHR object found in this environment.');
      }
    }
  }
})();

7、单例模式的弊端

了解了这么多关于单例的知识,我们再来看看它的弊端。

由于单例模式提供的是一种单点访问,所以它有可能导致模块间的强耦合。因此也就不利于单元测试了。

综上,单例还是留给定义命名空间和实现分支型方法这些用途。

通过七点不同方面对单例模式的介绍,大家是不是对单例模式有了更深入的了解,希望这篇文章可以帮到大家。

Javascript 相关文章推荐
JavaScript编程开发中的五个实用小技巧
Jul 22 Javascript
JQuery对class属性的操作实现按钮开关效果
Oct 11 Javascript
JQuery实现动态添加删除评论的方法
May 18 Javascript
javascript实现任务栏消息提示的简单实例
May 31 Javascript
JS实现的自定义水平滚动字体插件完整实例
Jun 17 Javascript
onmouseover事件和onmouseout事件全面理解
Aug 15 Javascript
JavaScript登录验证码的实现
Oct 27 Javascript
jQuery插件FusionCharts绘制的2D帕累托图效果示例【附demo源码】
Mar 28 jQuery
node.js中fs.stat与fs.fstat的区别详解
Jun 01 Javascript
前端html中jQuery实现对文本的搜索功能并把搜索相关内容显示出来
Nov 14 jQuery
layui 点击重置按钮, select 并没有被重置的解决方法
Sep 03 Javascript
jQuery实现手风琴特效
Jan 11 jQuery
javascript bom是什么及bom和dom的区别
Nov 26 #Javascript
Javascript模仿淘宝信用评价实例(附源码)
Nov 26 #Javascript
Javascript BOM学习小结(六)
Nov 26 #Javascript
js实现延时加载Flash的方法
Nov 26 #Javascript
学习JavaScript设计模式(链式调用)
Nov 26 #Javascript
学习JavaScript设计模式(继承)
Nov 26 #Javascript
js图片跟随鼠标移动代码
Nov 26 #Javascript
You might like
玩家交还《星际争霸》原始码光盘 暴雪报以厚礼
2017/05/05 星际争霸
PHP安全编程之加密功能
2006/10/09 PHP
判断Keep-Alive模式的HTTP请求的结束的实现代码
2011/08/06 PHP
php空间不支持socket但支持curl时recaptcha的用法
2011/11/07 PHP
PHP中数组的分组排序实例
2014/06/01 PHP
Laravel手动分页实现方法详解
2016/10/09 PHP
PHP实现读取文件夹及批量重命名文件操作示例
2019/04/15 PHP
Mootools 1.2教程 设置和获取样式表属性
2009/09/15 Javascript
js渐变显示渐变消失示例代码
2013/08/01 Javascript
Move.js入门
2017/02/08 Javascript
vue图片加载与显示默认图片实例代码
2017/03/16 Javascript
node.js中EJS 模板快速入门教程
2017/05/08 Javascript
Node做中转服务器转发接口
2017/10/18 Javascript
详解基于vue-cli配置移动端自适应
2018/01/13 Javascript
Vue+mui实现图片的本地缓存示例代码
2018/05/24 Javascript
JS使用对象的defineProperty进行变量监控操作示例
2019/02/02 Javascript
Vue中rem与postcss-pxtorem的应用详解
2019/11/20 Javascript
把MySQL表结构映射为Python中的对象的教程
2015/04/07 Python
Python实现完整的事务操作示例
2017/06/20 Python
Python PyQt5标准对话框用法示例
2017/08/23 Python
解决python Markdown模块乱码的问题
2019/02/14 Python
python面向对象实现名片管理系统文件版
2019/04/26 Python
PyQt5 QTableView设置某一列不可编辑的方法
2019/06/25 Python
Python实现一个带权无回置随机抽选函数的方法
2019/07/24 Python
python 实现将Numpy数组保存为图像
2020/01/09 Python
python读取xml文件方法解析
2020/08/04 Python
Python离线安装各种库及pip的方法
2020/11/28 Python
会计学个人自荐信模板
2013/12/13 职场文书
中专药剂专业应届毕的自我评价
2013/12/27 职场文书
自行车广告词大全
2014/03/21 职场文书
2014年个人债务授权委托书范本
2014/09/22 职场文书
2014年个人售房协议书
2014/10/30 职场文书
大学生违纪检讨书范文
2015/05/07 职场文书
信用卡工资证明范本
2015/06/19 职场文书
导游词之无锡丝业博物馆
2019/11/12 职场文书
Python异常类型以及处理方法汇总
2021/06/05 Python