资讯专栏INFORMATION COLUMN

JS设计模式-单例模式

Doyle / 2696人阅读

摘要:单例模式单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。透明的单例模式利用一个形成一个闭包,在里边通过变量来记录实例,并返回构造函数。

单例模式
单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。  
———来自维基百科

一个很典型的应用是在点击登录按钮,弹出登录浮窗,不论点击多少次登录按钮,页面始终只会弹出一个登录浮窗。

实现单例模式

思路很简单,用一个变量记录是否已经为某个类创建过对象,如果没有则返回新建的对象,反之则返回之前创建的对象。

在构造器里记录实例:
var Singleton = function(name) {
  this.name = name;
  this.instance = null;
};

Singleton.prototype.getName = function() {
  console.log(this.name);
};

Singleton.getInstance = function(name) {
  if (!this.instance) {
    this.instance = new Singleton(name);
  }
  return this.instance;
};

var a = Singleton.getInstance("sean1");
var b = Singleton.getInstance("sean2");
console.log(a === b); // true
使用闭包记录实例:
var Singleton = function(name) {
  this.name = name;
};

Singleton.prototype.getName = function() {
  console.log(this.name);
};

Singleton.getInstance = (function() {
  var instance = null;
  return function(name) {
    if (!instance) {
      instance = new Singleton(name);
    }
    return instance;
  };
})();
var a = Singleton.getInstance("sean1");
var b = Singleton.getInstance("sean2");
console.log(a === b); // true

以上方法相对简单,但是Singleton类的使用者必须知道这是一个单例类,需要调用getInstance()函数(而不是new的方式)来获取对象,这也就是增加了“不透明性”。

透明的单例模式

利用一个IIFE形成一个闭包,在里边通过变量instance来记录实例,并返回构造函数。

var CreateDiv = (function() {
  var instance;

  var CreateDiv = function(html) {
    if (instance) {
      return instance;
    }
    this.html = html;
    this.init();
    return (instance = this);
  };

  CreateDiv.prototype.init = function() {
    var div = document.createElement("div");
    div.innerHtml = this.html;
    document.body.appendChild(div);
  };

  return CreateDiv;
})();

var a = new CreateDiv("sean1");
var b = new CreateDiv("sean2");
console.log(a === b); // true

上面完成了一个透明的单例类的编写,但还是有缺点,增加了一些程序的复杂度,且阅读性差。还有重要的一点是违反了“单一职责原则”,接下来再改进一下。

用代理实现单例模式

其实就是将实际的业务代码与负责管理单例的代码分离,管理单例的类就是代理类。

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 ProxySingletonCreateDiv = (function() {
  var instance;
  return function(html) {
    if (!instance) {
      instance = new CreateDiv(html);
    }
    return instance;
  };
})();

var a1 = new ProxySingletonCreateDiv("sean1");
var b1 = new ProxySingletonCreateDiv("sean2");
console.log(a1 === b1); // true

// 如果想要创建多个 div 就直接调用 CreateDiv 咯

var a2 = new CreateDiv("sean1");
var b2 = new CreateDiv("sean2");
console.log(a2 === b2); // false
JavaScript 中的单例模式

前面提到的几种实现,更多的是接近传统面向对象语言中的实现,但 JavaScript 是一门无类(class-free)语言,我们只要记住单例模式但核心是 确保只有一个实例,并提供全局访问

全局变量不是单例模式,但它却满足单例的条件,所以我们经常会把全局变量当成单例来使用:var a = {};。但是我们都知道全局变量很容易造成命名空间污染、容易被不小心覆盖等问题。我们有必要尽量减少全局变量的使用,即使要,也要将污染降到最低:

使用命名空间

var namespace1 = {
  a: function() {
    return 1;
  },
  b: function() {
    return 2;
  }
};

// 或者动态创建命名空间:

var MyApp = {};
MyApp.namespace = function(name) {
  var parts = name.split(".");
  var current = MyApp;
  for (var item of parts) {
    if (!current[item]) {
      current[item] = {};
    }
    current = current[item];
  }
};
MyApp.namespace("event");
MyApp.namespace("dom.style");

console.dir(MyApp);

使用闭包封装私有变量

var user = (function() {
  var __name = "sean",
    __age = 19;
  return {
    getUserInfo: function() {
      return __name + "-" + __age;
    }
  };
})();

惰性单例

所谓惰性,就是只有在需要的时候才会去做。前面提到的也有满足惰性的实现,不过是基于“类”的。下面来看一下文章开头提到的那个典型的应用实现:


  
    
    
  

上面的代码完成了惰性单例的实现,但仍然违反了单一职责原则,如果我们下次不是创建一个登录浮窗而是别的元素呢,请往后看通用的惰性单例。

通用的惰性单例
// 定义一个管理单例的函数:
var getSingle = function(fn) {
  var result;
  return function() {
    return result || (result = fn.apply(this, arguments))
  }
}

// 定义创建浮窗的函数:
var createLoginLayer = function() {
  var div = document.createElement("div");
  div.innerHTML = "我是登录浮窗";
  div.style.display = "none";
  document.body.appendChild(div);
  return div;
}

// 获取单例的浮窗
var createSingleLoginLayer = getSingle(createLoginLayer);

document.getElementById("btn").onclick = function() {
  var loginLayer = createSingleLoginLayer();
  loginLayer.style.display = "block";
};
参考

曾探. JavaScript设计模式与开发实践 (图灵原创) (Chinese Edition)

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

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

相关文章

  • JS 单例模式

    摘要:但是如何在对构造函数使用操作符创建多个对象的时候仅获取一个单例对象呢。单例的实例引用单例构造函数单例私有属性和方法暴露出来的对象改进之前在构造函数中重写自身会丢失所有在初始定义和重定义之间添加到其中的属性。 1. 单例模式 单例模式 (Singleton) 的实现在于保证一个特定类只有一个实例,第二次使用同一个类创建新对象的时候,应该得到与第一次创建对象完全相同的对象。当创建一个新对象...

    姘存按 评论0 收藏0
  • 浅谈js单例模式

    摘要:单例模式说到单例设计模式,中经常使用的单例模式通常分两种,懒汉模式和饿汉模式懒汉模式简单写了下私有化构造函数在获取实例的方法中返回实例化对象虽然很多大佬都写过啦,但是小生为了加深记忆便再写一遍虽然实现了单例模式,但是未考虑到线程安全,多个线 java单例模式 说到单例设计模式,Java中经常使用java的单例模式通常分两种,懒汉模式和饿汉模式 懒汉模式 class singleDemo...

    draveness 评论0 收藏0
  • js设计模式--单例模式

    摘要:文章系列设计模式单例模式设计模式策略模式设计模式代理模式概念单例模式的定义是保证一个类仅有一个实例,并提供一个访问它的全局访问点。在开发中,单例模式的用途同样非常广泛。 前言 本系列文章主要根据《JavaScript设计模式与开发实践》整理而来,其中会加入了一些自己的思考。希望对大家有所帮助。 文章系列 js设计模式--单例模式 js设计模式--策略模式 js设计模式--代理模式 概念...

    CloudwiseAPM 评论0 收藏0
  • js常用设计模式实现(一)单例模式

    摘要:什么是设计模式设计模式是一种能够被反复使用,符合面向对象特性的代码设计经验的总结,合理的使用设计模式能够让你得代码更容易维护和可靠设计模式的类型共分为创建型模式,结构型模式,行为型模式三种创建型模式创建型模式是对一个类的实例化过程进行了抽象 什么是设计模式 设计模式是一种能够被反复使用,符合面向对象特性的代码设计经验的总结,合理的使用设计模式能够让你得代码更容易维护和可靠设计模式的类型...

    EscapedDog 评论0 收藏0
  • 从ES6重新认识JavaScript设计模式(一): 单例模式

    摘要:什么是单例模式单例模式是一种十分常用但却相对而言比较简单的单例模式。对象就是单例模式的体现。总结单例模式虽然简单,但是在项目中的应用场景却是相当多的,单例模式的核心是确保只有一个实例,并提供全局访问。 1. 什么是单例模式? 单例模式是一种十分常用但却相对而言比较简单的单例模式。它是指在一个类只能有一个实例,即使多次实例化该类,也只返回第一次实例化后的实例对象。单例模式不仅能减少不必要...

    G9YH 评论0 收藏0

发表评论

0条评论

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