学习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 相关文章推荐
ExtJS Window 最小化的一种方法
Nov 18 Javascript
收集的一些Array及String原型对象的扩展实现代码
Dec 05 Javascript
js调试系列 控制台命令行API使用方法
Jun 18 Javascript
jquery操作checkbox示例分享
Jul 21 Javascript
JavaScript截取字符串的2个函数介绍
Aug 27 Javascript
jQuery中index()方法用法实例
Dec 27 Javascript
jquery拖动层效果插件用法实例分析(附demo源码)
Apr 28 Javascript
jquery实现自适应banner焦点图
Feb 16 Javascript
Angular5集成eventbus的示例代码
Jul 19 Javascript
关于在LayUI中使用AJAX提交巨坑记录
Oct 25 Javascript
微信小程序顶部导航栏可滑动并选中放大
Dec 05 Javascript
Node.js 深度调试方法解析
Jul 28 Javascript
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
用Php实现链结人气统计
2006/10/09 PHP
收藏的一个php小偷的核心程序
2007/04/09 PHP
php dirname(__FILE__) 获取当前文件的绝对路径
2011/06/28 PHP
php遍历所有文件及文件夹的方法深入解析
2013/06/08 PHP
smarty中改进truncate使其支持中文的方法
2016/05/30 PHP
php 删除一维数组中某一个值元素的操作方法
2018/02/01 PHP
js活用事件触发对象动作
2008/08/10 Javascript
15个常用的jquery代码片段
2015/12/19 Javascript
Angularjs的Controller间通信机制实例分析
2016/11/07 Javascript
零基础轻松学JavaScript闭包
2016/12/30 Javascript
thinkjs 文件上传功能实例代码
2017/11/08 Javascript
vue 中 beforeRouteEnter 死循环的问题
2019/04/23 Javascript
python基础教程之简单入门说明(变量和控制语言使用方法)
2014/03/25 Python
python使用any判断一个对象是否为空的方法
2014/11/19 Python
浅析Python中元祖、列表和字典的区别
2016/08/17 Python
Python处理文本文件中控制字符的方法
2017/02/07 Python
轻松理解Python 中的 descriptor
2017/09/15 Python
Python实现的字典值比较功能示例
2018/01/08 Python
python3+PyQt5泛型委托详解
2018/04/24 Python
Linux(Redhat)安装python3.6虚拟环境(推荐)
2018/05/05 Python
使用Numpy读取CSV文件,并进行行列删除的操作方法
2018/07/04 Python
利用PyTorch实现VGG16教程
2020/06/24 Python
详解CSS3中Media Queries的相关使用
2015/07/17 HTML / CSS
Algenist奥杰尼官网:微藻抗衰老护肤品牌
2017/07/15 全球购物
GetYourGuide台湾:预订旅游活动、景点和旅游项目
2019/06/10 全球购物
应届生求职简历的自我评价怎么写
2013/10/23 职场文书
大学军训感言1000字
2014/02/25 职场文书
一岗双责责任书
2014/04/15 职场文书
《会走路的树》教后反思
2014/04/19 职场文书
学生无故旷课检讨书
2014/09/20 职场文书
学校证明范文
2015/06/24 职场文书
学习党史心得体会2016
2016/01/23 职场文书
2019年怎样才能撰写出优秀的自荐信
2019/03/25 职场文书
2019年聘任书的写作格式及范文!
2019/07/03 职场文书
SQL基础的查询语句
2021/11/11 MySQL
Linux磁盘管理方法介绍
2022/06/01 Servers