javascript设计模式之单体模式学习笔记


Posted in Javascript onFebruary 15, 2017

单体模式提供了一种将代码组织为一个逻辑单元的手段,这个逻辑单元中的代码可以通过单一变量进行访问。

单体模式的优点是:

  • 可以用来划分命名空间,减少全局变量的数量。
  • 使用单体模式可以使代码组织的更为一致,使代码容易阅读和维护。
  • 可以被实例化,且实例化一次。

什么是单体模式?单体模式是一个用来划分命名空间并将一批属性和方法组织在一起的对象,如果它可以被实例化,那么它只能被实例化一次。

但是并非所有的对象字面量都是单体,比如说模拟数组或容纳数据的话,那么它就不是单体,但是如果是组织一批相关的属性和方法在一起的话,那么它有可能是单体模式,所以这需要看开发者编写代码的意图;

下面我们来看看定义一个对象字面量(结构类似于单体模式)的基本结构如下:

// 对象字面量
var Singleton = {
  attr1: 1,
  attr2: 2,
  method1: function(){
    return this.attr1;
  },
  method2: function(){
    return this.attr2;
  }
};

如上面只是简单的字面量结构,上面的所有成员变量都是通过Singleton来访问的,但是它并不是单体模式;因为单体模式还有一个更重要的特点,就是可以仅被实例化一次,上面的只是不能被实例化的一个类,因此不是单体模式;对象字面量是用来创建单体模式的方法之一;

使用单体模式的结构如下demo

我们明白的是单体模式如果有实例化的话,那么只实例化一次,要实现一个单体模式的话,我们无非就是使用一个变量来标识该类是否被实例化,如果未被实例化的话,那么我们可以实例化一次,否则的话,直接返回已经被实例化的对象。

如下代码是单体模式的基本结构:

// 单体模式
var Singleton = function(name){
  this.name = name;
  this.instance = null;
};
Singleton.prototype.getName = function(){
  return this.name;
}
// 获取实例对象
function getInstance(name) {
  if(!this.instance) {
    this.instance = new Singleton(name);
  }
  return this.instance;
}
// 测试单体模式的实例
var a = getInstance("aa");
var b = getInstance("bb");
// 因为单体模式是只实例化一次,所以下面的实例是相等的
console.log(a === b); // true

由于单体模式只实例化一次,因此第一次调用,返回的是a实例对象,当我们继续调用的时候,b的实例就是a的实例,因此下面都是打印的是aa;

console.log(a.getName());// aa
console.log(b.getName());// aa

上面的封装单体模式也可以改成如下结构写法:

// 单体模式
var Singleton = function(name){
  this.name = name;
};
Singleton.prototype.getName = function(){
  return this.name;
}
// 获取实例对象
var getInstance = (function() {
  var instance = null;
  return function(name) {
    if(!instance) {
      instance = new Singleton(name);
    }
    return instance;
  }
})();
// 测试单体模式的实例
var a = getInstance("aa");
var b = getInstance("bb");
// 因为单体模式是只实例化一次,所以下面的实例是相等的

console.log(a === b); // true

console.log(a.getName());// aa

console.log(b.getName());// aa

理解使用代理实现单列模式的好处

比如我现在页面上需要创建一个div的元素,那么我们肯定需要有一个创建div的函数,而现在我只需要这个函数只负责创建div元素,其他的它不想管,也就是想实现单一职责原则,就好比淘宝的kissy一样,一开始的时候他们定义kissy只做一件事,并且把这件事做好,具体的单体模式中的实例化类的事情交给代理函数去处理,这样做的好处是具体的业务逻辑分开了,代理只管代理的业务逻辑,在这里代理的作用是实例化对象,并且只实例化一次; 创建div代码只管创建div,其他的不管;如下代码:

// 单体模式
var CreateDiv = function(html) {
  this.html = html;
  this.init();
}
CreateDiv.prototype.init = function(){
  var div = document.createElement("div");
  div.innerHTML = this.html;
  document.body.appendChild(div);
};
// 代理实现单体模式
var ProxyMode = (function(){
  var instance;
  return function(html) {
    if(!instance) {
      instance = new CreateDiv("我来测试下");
    }
    return instance;
  } 
})();
var a = new ProxyMode("aaa");
var b = new ProxyMode("bbb");
console.log(a===b);// true

理解使用单体模式来实现弹窗的基本原理

下面我们继续来使用单体模式来实现一个弹窗的demo;我们先不讨论使用单体模式来实现,我们想下我们平时是怎么编写代码来实现弹窗效果的; 比如我们有一个弹窗,默认的情况下肯定是隐藏的,当我点击的时候,它需要显示出来;如下编写代码:

// 实现弹窗
var createWindow = function(){
  var div = document.createElement("div");
  div.innerHTML = "我是弹窗内容";
  div.style.display = 'none';
  document.body.appendChild('div');
  return div;
};
document.getElementById("Id").onclick = function(){
  // 点击后先创建一个div元素
  var win = createWindow();
  win.style.display = "block";
}

如上的代码;大家可以看看,有明显的缺点,比如我点击一个元素需要创建一个div,我点击第二个元素又会创建一次div,我们频繁的点击某某元素,他们会频繁的创建div的元素,虽然当我们点击关闭的时候可以移除弹出代码,但是呢我们频繁的创建和删除并不好,特别对于性能会有很大的影响,对DOM频繁的操作会引起重绘等,从而影响性能;因此这是非常不好的习惯;我们现在可以使用单体模式来实现弹窗效果,我们只实例化一次就可以了;如下代码:

// 实现单体模式弹窗
var createWindow = (function(){
  var div;
  return function(){
    if(!div) {
      div = document.createElement("div");
      div.innerHTML = "我是弹窗内容";
      div.style.display = 'none';
      document.body.appendChild(div);
    }
    return div;
  }
})();
document.getElementById("Id").onclick = function(){
  // 点击后先创建一个div元素
  var win = createWindow();
  win.style.display = "block";
}

理解编写通用的单体模式

上面的弹窗的代码虽然完成了使用单体模式创建弹窗效果,但是代码并不通用,比如上面是完成弹窗的代码,假如我们以后需要在页面中一个iframe呢?我们是不是需要重新写一套创建iframe的代码呢?比如如下创建iframe:

var createIframe = (function(){
  var iframe;
  return function(){
    if(!iframe) {
      iframe = document.createElement("iframe");
      iframe.style.display = 'none';
      document.body.appendChild(iframe);
    }
    return iframe;
  };
})();

我们看到如上代码,创建div的代码和创建iframe代码很类似,我们现在可以考虑把通用的代码分离出来,使代码变成完全抽象,我们现在可以编写一套代码封装在getInstance函数内,如下代码:

var getInstance = function(fn) {
  var result;
  return function(){
    return result || (result = fn.call(this,arguments));
  }
};

如上代码:我们使用一个参数fn传递进去,如果有result这个实例的话,直接返回,否则的话,当前的getInstance函数调用fn这个函数,是this指针指向与这个fn这个函数;之后返回被保存在result里面;现在我们可以传递一个函数进去,不管他是创建div也好,还是创建iframe也好,总之如果是这种的话,都可以使用getInstance来获取他们的实例对象;

如下测试创建iframe和创建div的代码如下:

// 创建div
var createWindow = function(){
  var div = document.createElement("div");
  div.innerHTML = "我是弹窗内容";
  div.style.display = 'none';
  document.body.appendChild(div);
  return div;
};
// 创建iframe
var createIframe = function(){
  var iframe = document.createElement("iframe");
  document.body.appendChild(iframe);
  return iframe;
};
// 获取实例的封装代码
var getInstance = function(fn) {
  var result;
  return function(){
    return result || (result = fn.call(this,arguments));
  }
};
// 测试创建div
var createSingleDiv = getInstance(createWindow);
document.getElementById("Id").onclick = function(){
  var win = createSingleDiv();
  win.style.display = "block";
};
// 测试创建iframe
var createSingleIframe = getInstance(createIframe);
document.getElementById("Id").onclick = function(){
  var win = createSingleIframe();
  win.src = "http://www.3water.com";
};

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Jquery 基础学习笔记之文档处理
May 29 Javascript
jQuery EasyUI API 中文文档 - Pagination分页
Sep 29 Javascript
js 页面元素的几个用法总结
Nov 18 Javascript
详解JavaScript操作HTML DOM的基本方式
Oct 21 Javascript
JS实现获取剪贴板内容的方法
Jun 21 Javascript
Bootstrap页面标题Page Header的实现方法
Mar 22 Javascript
理解 Node.js 事件驱动机制的原理
Aug 16 Javascript
详解vue mixins和extends的巧妙用法
Dec 20 Javascript
Vue数据双向绑定原理及简单实现方法
May 18 Javascript
ES6函数实现排它两种写法解析
May 13 Javascript
vue中jsonp插件的使用方法示例
Sep 10 Javascript
利用JS判断元素是否为数组的方法示例
Jan 08 Javascript
JavaScript中的toString()和toLocaleString()方法的区别
Feb 15 #Javascript
javascript设计模式之模块模式学习笔记
Feb 15 #Javascript
javascript设计模式之策略模式学习笔记
Feb 15 #Javascript
BootStrap实现带关闭按钮功能
Feb 15 #Javascript
Jil,高效的json序列化和反序列化库
Feb 15 #Javascript
Bootstrap模态框使用详解
Feb 15 #Javascript
js时间戳和c#时间戳互转方法(推荐)
Feb 15 #Javascript
You might like
PHP提取数据库内容中的图片地址并循环输出
2010/03/21 PHP
php的urlencode()URL编码函数浅析
2011/08/09 PHP
解析VS2010利用VS.PHP插件调试PHP的方法
2013/07/19 PHP
大家须知简单的php性能优化注意点
2016/01/04 PHP
PHP 实现从数据库导出到.csv文件方法
2017/07/06 PHP
php 读写json文件及修改json的方法
2018/03/07 PHP
PHP单例模式数据库连接类与页面静态化实现方法
2019/03/20 PHP
自写简单JS判断是否已经弹出页面
2010/10/20 Javascript
五个jQuery图片画廊插件 推荐
2011/05/12 Javascript
Jquery操作下拉框(DropDownList)实现取值赋值
2013/08/13 Javascript
jQuery 获取跨域XML(RSS)数据的相关总结分析
2016/05/18 Javascript
vue项目中做编辑功能传递数据时遇到问题的解决方法
2016/12/19 Javascript
如何制作幻灯片(代码分享)
2017/01/06 Javascript
JS返回顶部实例代码
2020/08/09 Javascript
Angular4编程之表单响应功能示例
2017/12/13 Javascript
vue2.0项目实现路由跳转的方法详解
2018/06/21 Javascript
基于JS实现前端压缩上传图片的实例代码
2019/05/14 Javascript
vue实现倒计时获取验证码效果
2020/04/17 Javascript
微信小程序如何通过用户授权获取手机号(getPhoneNumber)
2020/01/21 Javascript
[44:58]2018DOTA2亚洲邀请赛 4.5 淘汰赛 LGD vs Liquid 第二场
2018/04/06 DOTA
Python的类实例属性访问规则探讨
2015/01/30 Python
python使用celery实现异步任务执行的例子
2019/08/28 Python
简单了解python列表和元组的区别
2020/05/14 Python
Python应用实现双指数函数及拟合代码实例
2020/06/19 Python
Python非单向递归函数如何返回全部结果
2020/12/18 Python
Python jieba库分词模式实例用法
2021/01/13 Python
css3 box-shadow阴影(外阴影与外发光)图示讲解
2017/08/11 HTML / CSS
基于HTML5的WebGL实现json和echarts图表展现在同一个界面
2017/10/26 HTML / CSS
Java模拟试题
2014/11/10 面试题
办公室前台岗位职责
2014/01/04 职场文书
尊老爱亲美德少年事迹材料
2014/08/14 职场文书
2014年控辍保学工作总结
2014/12/08 职场文书
刑事案件上诉状
2015/05/23 职场文书
2015大学生暑期实习报告
2015/07/13 职场文书
幼儿园2016圣诞节活动总结
2016/03/31 职场文书
二手手机买卖合同范本(2019年版)
2019/10/28 职场文书