学习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 相关文章推荐
Google排名中的10个最著名的 JavaScript库
Apr 27 Javascript
对于this和$(this)的个人理解
Sep 08 Javascript
用jQuery实现的智能隐藏、滑动效果的返回顶部代码
Mar 18 Javascript
Node.js中使用事件发射器模式实现事件绑定详解
Aug 15 Javascript
详解JavaScript中setSeconds()方法的使用
Jun 11 Javascript
jQuery实现背景弹性滚动的导航效果
Jun 01 Javascript
weex slider实现滑动底部导航功能
Aug 28 Javascript
react-native使用react-navigation进行页面跳转导航的示例
Sep 07 Javascript
如何解决React官方脚手架不支持Less的问题(小结)
Sep 12 Javascript
浅谈vuex actions和mutation的异曲同工
Dec 13 Javascript
JS三级联动代码格式实例详解
Dec 30 Javascript
vue开发简单上传图片功能
Jun 30 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中取得image按钮传递的name值
2006/10/09 PHP
php session 错误
2009/05/21 PHP
php 自写函数代码 获取关键字 去超链接
2010/02/08 PHP
PHP中cookie和session的区别实例分析
2014/08/28 PHP
php获取QQ头像并显示的方法
2014/12/23 PHP
php从文件夹随机读取文件的方法
2015/06/01 PHP
thinkphp ajaxfileupload实现异步上传图片的示例
2017/08/28 PHP
YII框架行为behaviors用法示例
2019/04/26 PHP
javascript IFrame 强制刷新代码
2009/07/23 Javascript
关于jQuery对象数据缓存Cache原理以及jQuery.data详解
2013/04/07 Javascript
简单实用的全选反选按钮例子
2013/10/18 Javascript
javascript中JSON.parse()与eval()解析json的区别
2016/05/19 Javascript
angularJS 如何读写缓冲的方法(推荐)
2016/08/06 Javascript
BootStrap表单时间选择器详解
2017/05/09 Javascript
bootstrap select下拉搜索插件使用方法详解
2017/11/23 Javascript
使用Angular 6创建各种动画效果的方法
2018/10/10 Javascript
微信小程序获取用户信息的两种方法wx.getUserInfo与open-data实例分析
2019/05/03 Javascript
Vue快速实现通用表单验证的示例代码
2020/01/09 Javascript
React Native登录之指纹登录篇的示例代码
2020/11/03 Javascript
python解析中国天气网的天气数据
2014/03/21 Python
python两种遍历字典(dict)的方法比较
2014/05/29 Python
Python异常处理操作实例详解
2018/05/10 Python
解决Pycharm下面出现No R interpreter defined的问题
2018/10/29 Python
python 求一个列表中所有元素的乘积实例
2019/06/11 Python
python网络编程 使用UDP、TCP协议收发信息详解
2019/08/29 Python
解决pytorch DataLoader num_workers出现的问题
2020/01/14 Python
Python爬虫实现selenium处理iframe作用域问题
2021/01/27 Python
美国室内盆栽植物购买网站:Plants.com
2020/04/24 全球购物
会计学生自我鉴定
2014/02/06 职场文书
最经典的商业地产项目广告词
2014/03/13 职场文书
春节慰问信范文
2015/02/15 职场文书
写给导师的自荐信
2015/03/06 职场文书
2015年感恩父亲节演讲稿
2015/03/19 职场文书
电影地道战观后感
2015/06/04 职场文书
高二化学教学反思
2016/02/22 职场文书
关于pytest结合csv模块实现csv格式的数据驱动问题
2022/05/30 Python