学习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 相关文章推荐
仿微博字符限制效果实现代码
Apr 20 Javascript
基于jquery实现一张图片点击鼠标放大再点缩小
Sep 29 Javascript
jQuery中的height innerHeight outerHeight区别示例介绍
Jun 15 Javascript
复制网页内容,粘贴之后自动加上网址的实现方法(脚本之家特别整理)
Oct 16 Javascript
jQuery的one()方法用法实例
Jan 19 Javascript
js实现图片无缝滚动特效
Mar 19 Javascript
jquery通过name属性取值的简单实现方法
Jun 20 Javascript
JS前端笔试题分析
Dec 19 Javascript
webpack引入eslint配置详解
Jan 22 Javascript
vue获取当前点击的元素并传值的实例
Mar 09 Javascript
从零开始搭建webpack+react开发环境的详细步骤
May 18 Javascript
在mpvue框架中使用Vant WeappUI组件库的注意事项【推进】
Jun 09 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 MSSQL 存储过程的方法
2008/12/24 PHP
浅析PHP中的字符串编码转换(自动识别原编码)
2013/07/02 PHP
PHP批量获取网页中所有固定种子链接的方法
2016/11/18 PHP
PHP分页显示的方法分析【附PHP通用分页类】
2018/05/10 PHP
PHP实现的日历功能示例
2018/09/01 PHP
PHP智能识别收货地址信息实例
2019/01/05 PHP
showModelessDialog()使用详解
2006/09/07 Javascript
javascript游戏开发之《三国志曹操传》零部件开发(五)可移动地图的实现
2013/01/23 Javascript
jquery ready函数、css函数及text()使用示例
2013/09/27 Javascript
javascript数组操作总结和属性、方法介绍
2014/04/05 Javascript
Js实现网页键盘控制翻页的方法
2014/10/30 Javascript
JavaScript跨浏览器获取页面中相同class节点的方法
2015/03/03 Javascript
Javascript实现网络监测的方法
2015/07/31 Javascript
jQuery实现标题有打字效果的焦点图代码
2015/11/16 Javascript
探讨JavaScript标签位置的存放与功能有无关系
2016/01/15 Javascript
修复jQuery tablesorter无法正确排序的bug(加千分位数字后)
2016/03/30 Javascript
jQuery extend()详解及简单实例
2017/05/06 jQuery
vue页面使用阿里oss上传功能的实例(一)
2017/08/09 Javascript
vue 2.0项目中如何引入element-ui详解
2017/09/06 Javascript
Nodejs封装类似express框架的路由实例详解
2020/01/05 NodeJs
JS深入学习之数组对象排序操作示例
2020/05/01 Javascript
解决Vue的文本编辑器 vue-quill-editor 小图标样式排布错乱问题
2020/08/03 Javascript
微信小程序通过websocket实时语音识别的实现代码
2020/08/19 Javascript
[01:03:36]Ti4 循环赛第三日DK vs Titan
2014/07/12 DOTA
用Python编写一个简单的FUSE文件系统的教程
2015/04/02 Python
python批量处理文件或文件夹
2020/07/28 Python
中科软测试工程师面试题
2012/06/16 面试题
大学生自助营养快餐店创业计划书
2014/01/13 职场文书
八项规定整改措施
2014/02/12 职场文书
观看信仰心得体会
2014/09/04 职场文书
教师党员整改措施
2014/10/24 职场文书
八项规定自查自纠报告及整改措施
2014/10/26 职场文书
化验室岗位职责
2015/02/14 职场文书
2015年教务主任工作总结
2015/07/22 职场文书
MySQL中VARCHAR与CHAR格式数据的区别
2021/05/26 MySQL
使用@Value值注入及配置文件组件扫描
2021/07/09 Java/Android