资讯专栏INFORMATION COLUMN

javaScript设计模式系列(二) 单体模式

ZoomQuiet / 2549人阅读

摘要:使用构造函数如果被实例化过,就返回实例化的结果这种方式比较简洁,通过判断是否已经实例化过,但是也没啥安全性,如果在外部修改那很可能单例模式就被破坏了。

摘要

单体模式也有人称为单例模式,是javaScript 中最基本但又最有用的模式之一。

什么是单体

一讲到概念第一反应就是WTF

其实单体模式是最常用,也是最有用的一种模式。而我们也会在项目中不知不觉的写一些单体模式,只是我们没有意识到,这也是一种设计模式。看一个简单的例子:

var zoom = {
  bird: 10,
  monkey: 10,
  play: function() {},
  eat: function() {}
}

这和我随手找的一个对象有什么区别,从严格意义上讲这确实不能称为一个单体。但我们可以分析一下这个对象,其中有两个zoom相关的属性,和两个zoom相关的方法。实际上最简单的单体就是字面量,它是把一些有一定关系的方法和属性组织在一起。

但他违背了面向对象设计的一条原则:类可以被扩展,但不应该被修改。听到这里我们也不需要慌,因为(Python,Ruby)都允许在定义了类之后对其修改。

可能你还是不知道单体与普通字面量有什么不同,那就再看一个例子:

var zoom = function() {
  var bird = 10;
  var monkey = 10;
  this.play = function() {}
  this.eat = function() {}
}
var zoom1 = new zoom();
var zoom2 = new zoom();
console.log(zoom1 === zoom2)//false;

zoom被实例化两次,但两个实例明显不相等。因为zoom1,zoom2的指针,并没有只向内存中的同一地址。这也正好可以引出单体与不同字面量的不同。按传统定义,单体是一个只能被实例化一次,并且可以通过一个众所周知的访问点访问的对象。后半句简单说,就是可以全局访问。

总结一下上面内容。单体是一个用来划分命名空间并将一批相关方法和属性组织在一起的对象,如果它可以被实例化,那么它只能被实例化一次。

与命名空间的关系

如果我们的变量都定义在全局中,一不小心别人重新用相同的变量名,定义一个变量,那我们的代码就会出问题,而且这种问题很难排查。为了避免这种不确定性,我们可以把代码组织在命名空间中(其实,就是另一个对象中,但这个对象,更具有包含性)

var ZoomSpace = {
  otherFunction: function() {}
}
ZoomSpace.zoomUtil = {
  bird: 10,
  monkey: 10,
  play: function() {},
  eat: function() {}
}

这样如果在全局中重定义了zoomUtil,我们的zoomUtil也不会受到影响。

使用构造函数
function zoomUtil() {
  //如果被实例化过,就返回实例化的结果
  if(zoomUtil.unique !== undefined) {
    return zoomUtil.unique;
  }
  var bird = 10;
  var monkey = 10;
  function play() {};
  function eat(){};
  return zoomUtil.unique = this;
}
var zoom1 = new zoomUtil();
var zoom2 = new zoomUtil();
console.log(zoom1 === zoom2)//true

这种方式比较简洁,通过判断是否已经实例化过,但是也没啥安全性,如果在外部修改zoomUtil.unique 那很可能单例模式就被破坏了。

拥有私有成员的单体 使用下划线表示

下划线表示私有成员,是约定俗称的一种方法,告诉别人不要直接访问私有方法。

使用上面我们已经定义好的命名空间:

ZoomSpace.zoomUtil = {
  bird: 10,
  monkey: 10,
  _runTime: function() {
    return ++this.bird;
  },
  play: function() {
    //return this._runTime(); 尽量不要再共有方法中使用this
    //this很可能因为此方法使用在别的函数中导致指向window
    return ZoomSpace.zoomUtil._runTime();
  },
  eat: function() {}
}
console.log(ZoomSpace.zoomUtil.play());//11

这种方法的安全性也不好,主要靠人为的约束,但是如果有人非要用我们的私有方法,导致我们删除或者修改私有方法后,他的程序不能用,只能能跟他说no zuo no die。

使用闭包

之前我们的单体都是这样的:

ZoomSpace.zoomUitl = {};

现在我们用一个立即执行的函数创建,像这样:

ZoomSpace.zoomUitl = (function(){
  return {};
})();

以往的做法是把变量和函数定义在构造函数内部。因此,每次生成一个实例,所有的方法属性都会被再次创建。这样的做法很低效。而现在的做法我们不必要担心定义的属性过多,因为它只会被实例化一次。我们来改写上面的例子:

ZoomSpace.zoomUtil = (function(){
  //私有属性和方法
  var bird = 10;
  var monkey = 10;
  function runTime(){
    return ++bird;//不需要使用this
  };
  function otherFunction(){};
  return {
    play:function(){
      return runTime();
    }
  }
})()
console.log(ZoomSpace.zoomUtil.play());//11

如果你想通过new的方式来写,可以像下面这样修改:

var ZoomSpace = {};
ZoomSpace.zoomUtil = (function() {
//私有属性和方法
  var unique; //唯一值
  function zoomUtilConstructor() {
    // 是否被实例化过
    if(unique.constructor === zoomUtilConstructor) {    
      //如果实例化过,返回已经实例化的对象
      return unique;
    }
    var bird = 10;
    var monkey = 10;
    function runTime() {
      return ++bird;
    };
    this.play = function(){
      return runTime();
    }
    // 保存实例化的对象
    return unique = this;
  }
  // 保存构造函数
  return unique = zoomUtilConstructor;
})()
var zoom1 = new ZoomSpace.zoomUtil();
var zoom2 = new ZoomSpace.zoomUtil();
console.log(zoom1 === zoom2); //true

方法有很多,具体看那种最适合。

总结

考虑否严格的只需要一个实例对象的类,那么就要考虑使用单体模式。使用数据缓存来存储该单例,用作判断单例是否已经生成,是单例模式主要的实现思路。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/87296.html

相关文章

  • JavaScript 设计模式读书笔记(四)——单体模式和链式调用

    摘要:单体模式在多种设计模式中,单体模式是最简单,也是最基础的设计模式。和之前说到的下划线表示私用成员方法比较起来,最大的优点就是可以创建真正的私用成员,使其不会在构造函数之外被随意修改。 单体模式 在多种Javascript设计模式中,单体模式是最简单,也是最基础的设计模式。它基础到似乎不太像是一种设计模式,因为我们在编写代码的过程中随时都会用到,并不需要过多思考,这是它简单的一面。同时...

    DevWiki 评论0 收藏0
  • 微服务指南走北():微服务架构的进程间通信(IPC)

    摘要:微服务常用的进程间通信技术即表述性状态传递英文,简称是博士在年他的博士论文中提出来的一种软件架构风格。摘自微服务实战从架构到部署处理部分请求失败对于分布式的微服务,必须要面对的一大问题就是局部请求失败的处理。 先抛出几个问题 微服务架构的交互模式有哪些? 微服务常用的进程间通信技术有哪些? 如何处理部分请求失败? API的定义需要注意的事项有哪些 微服务的通信机制与SOA的通信机制之...

    beanlam 评论0 收藏0
  • JavaScript_设计模式

    摘要:设计模式是一套可复用的,被众人知晓,经过编目分明的,经验的总结。创建类安全工厂判断是否调用关键字设计模式设计模式运算符可以用来判断某个构造函数的属性所指向的對象是否存在于另外一个要检测对象的原型链上。 设计模式 是一套可复用的,被众人知晓,经过编目分明的,经验的总结。作用:使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性 模式类型 创建型设计模式:解决对象在创建时产...

    BDEEFE 评论0 收藏0
  • JavaScript 模式》知识点小抄本(上)

    摘要:单体模式有以下优点用来划分命名空间,减少全局变量数量。通常我们使用操作符创建单体模式的三种选择,让构造函数总返回最初的对象使用全局对象来存储该实例不推荐,容易全局污染。实现该工厂模式并不困难,主要是要找到能够穿件所需类型对象的构造函数。 介绍 最近开始给自己每周订个学习任务,学习结果反馈为一篇文章的输出,做好学习记录。 这一周(02.25-03.03)我定的目标是《JavaScri...

    didikee 评论0 收藏0
  • 细谈JavaScript中的一些设计模式

    摘要:注意事项声明函数时候处理业务逻辑区分和单例的区别,配合单例实现初始化构造函数大写字母开头推荐注意的成本。简单工厂模式使用一个类通常为单体来生成实例。 @(书籍阅读)[JavaScript, 设计模式] 常见设计模式 一直对设计模式不太懂,花了一下午加一晚上的时间,好好的看了看各种设计模式,并总结了一下。 设计模式简介 设计模式概念解读 设计模式的发展与在JavaScript中的应用 ...

    30e8336b8229 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<