js设计模式——单例模式

Posted 陈宜栋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js设计模式——单例模式相关的知识,希望对你有一定的参考价值。

定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

Vue 中对应的体现就是 Vuex,一个 Vue 实例只会有一个全局的 Store。

一、javascript 中的单例模式

在 JavaScript 开发中,我们经常会把全局变量当成单例来使用,但是这样容易造成命名空间污染。我们有必要尽量减少全局变量的使用,即使需要,也应该把污染降到最低。

1. 使用命名空间

适当的使用命名空间,并不会杜绝全局变量,但可以减少全局变量的数量。

let MyApp = ;

MyApp.namespace = (name) => 
  const parts = name.split('.')
  let current = MyApp
  for (var i in parts) 
    const key = parts[i]
    if ( !current[key] ) 
      current[key] = 
    
    current = current[key]
  
;

MyApp.namespace('event')
MyApp.namespace('dom.stype')

console.dir(MyApp)

// 上述代码等价于

let MyApp = 
  event: ,
  dom: 
    style: 
  
;

2. 使用闭包封装私有变量

把一些变量封装到闭包内部,只暴露一些接口跟外界通信:

const user = (() => 
  const _name = 'sven', _age = 29
  return 
    getUserInfo: () => 
      return _name + '-' + _age
    
  
)();

console.log(user.getUserInfo())

二、惰性单例

惰性单例指的是在需要的时候才创建对象实例。

// 管理单例
const getSingle = function(fn) 
  let result
  return () => 
    return result || ( result = fn.apply(this, arguments) )
  


const createLoginLayer = () => 
  const div = document.createElement('div')
  div.innerhtml = '我是登录浮窗'
  document.body.appendChild(div)
  return div


const createSingleLoginLayer = getSingle(createLoginLayer)
var loginLayer = createSingleLoginLayer()
var loginLayer = createSingleLoginLayer()
var loginLayer = createSingleLoginLayer()


const createSingleIframe = getSingle(() => 
  const iframe = document.createElement('iframe')
  document.body.appendChild(iframe)
  return iframe
)
var iframe = createSingleIframe()
var iframe = createSingleIframe()
var iframe = createSingleIframe()

三、例子

  • 描述(实现一个 Storage)
    实现 Storage,使得该对象为单例,基于 localStorage 进行封装。实现方法 setItem(key,value) 和 getItem(key)。

  • 实现 1 (闭包实现 Storage)

function StoreBase()
StoreBase.prototype.getItem = (key) => 
  return localStorage.getItem(key)

StoreBase.prototype.setItem = (key, val) => 
  localStorage.setItem(key, val)


const Store = (() => 
  let instance = null
  return () => 
    if (!instance) 
      instance = new StoreBase()
    
    return instance
  
)()

const store1 = Store()
const store2 = Store()

store1.setItem('name', '张三')

store2.getItem('name') // 张三
store1.getItem('name') // 张三

console.log(store1 === store2) //
  • 实现 2 (静态类实现 Storage)
class Storage 
  static getInstance() 
    if (!Storage.instance) 
      Storage.instance = new Storage()
    
    return Storage.instance
  
  getItem(key) 
    return localStorage.getItem(key)
  
  setItem(key, value) 
    localStorage.setItem(key, value)
  


const storage1 = Storage.getInstance()
const storage2 = Storage.getInstance()

storage1.setItem('name', '张三')
storage1.getItem('name')
storage2.getItem('name')

console.log(storage1 === storage2)

JS设计模式一:单例模式

单例模式

 

    单例模式也称作为单子模式,更多的也叫做单体模式。为软件设计中较为简单但是最为常用的一种设计模式。

    下面是维基百科对单例模式的介绍:

    在应用单例模式时,生成单例的类必须保证只有一个实例的存在,很多时候整个系统只需要拥有一个全局对象,才有利于协调系统整体的行为。比如在整个系统的配置文件中,配置数据有一个单例对象进行统一读取和修改,其他对象需要配置数据的时候也统一通过该单例对象来获取配置数据,这样就可以简化复杂环境下的配置管理。

    单例模式的思路是:一个类能返回一个对象的引用(并且永远是同一个)和一个获得该实例的方法(静态方法,通常使用 getInstance 名称)。那么当我们调用这个方法时,如果类持有的引用不为空就返回该引用,否者就创建该类的实例,并且将实例引用赋值给该类保持的那个引用再返回。同时将该类的构造函数定义为私有方法,避免其他函数使用该构造函数来实例化对象,只通过该类的静态方法来得到该类的唯一实例。

    对于 JS 来说,巨大的灵活性使得其可以有多种方式实现单例模式,使用闭包方式来模拟私有数据,按照其思路可得:

 

  1. var single = (function(){

  2.     var unique;

  3.  

  4.     function getInstance(){

  5.         if( unique === undefined ){

  6.             unique = new Construct();

  7.         }

  8.         return unique;

  9.     }

  10.  

  11.     function Construct(){

  12.         // ... 生成单例的构造函数的代码

  13.     }

  14.  

  15.     return {

  16.         getInstance : getInstance

  17.     }

  18. })();

    以上,unique便是返回对象的引用,而 getInstance便是静态方法获得实例。Construct 便是创建实例的构造函数。

    可以通过 single.getInstance() 来获取到单例,并且每次调用均获取到同一个单例。这就是 单例模式 所实现的效果。

    不过,对于JS来说,显然以上循规蹈矩的方式显得过于笨重,在不同的场景以不同的方式实现单体模式正是 JS 的优势

    

    实现1: 最简单的对象字面量

 

  1. var singleton = {

  2.         attr : 1,

  3.         method : function(){ return this.attr; }

  4.     }

  5.  

  6. var t1 = singleton ;

  7. var t2 = singleton ;

    那么很显然的, t1 === t2 。

    十分简单,并且非常使用,不足之处在于没有什么封装性,所有的属性方法都是暴露的。对于一些需要使用私有变量的情况就显得心有余而力不足了。当然在对于 this 的问题上也是有一定弊端的。

    实现2:构造函数内部判断

    其实和最初的JS实现有点类似,不过是将对是否已经存在该类的实例的判断放入构造函数内部。

 

  1. function Construct(){

  2.     // 确保只有单例

  3.     if( Construct.unique !== undefined ){

  4.         return Construct.unique; 

  5.     }

  6.     // 其他代码

  7.     this.name = "NYF";

  8.     this.age="24";

  9.     Construct.unique = this;

  10. }

  11.  

  12. var t1 = new Construct() ;

  13. var t2 = new Construct() ;

    那么也有的, t1 === t2 。

    也是非常简单,无非就是提出一个属性来做判断,但是该方式也没有安全性,一旦我在外部修改了Construct的unique属性,那么单例模式也就被破坏了。

    

    实现3 : 闭包方式    

    对于大着 灵活 牌子的JS来说,任何问题都能找到 n 种答案,只不过让我自己去掂量孰优孰劣而已,下面就简单的举几个使用闭包实现单例模式的方法,无非也就是将创建了的单例缓存而已。

 

  1. var single = (function(){

  2.     var unique;

  3.     function Construct(){

  4.         // ... 生成单例的构造函数的代码

  5.     }

  6.  

  7.     unique = new Constuct();

  8.  

  9.     return unique;

  10. })();

 

    只要 每次讲 var t1 = single; var t2 = single;即可。 与对象字面量方式类似。不过相对而言更安全一点,当然也不是绝对安全。

    如果希望会用调用 single() 方式来使用,那么也只需要将内部的 return 改为

    

    return function(){

        return unique;

    } 

    以上方式也可以使用 new 的方式来进行(形式主义的赶脚)。当然这边只是给了闭包的一种例子而已,也可以在 Construct 中判断单例是否存在 等等。 各种方式在各个不同情况做好选着即可。

总结

    总的来说,单例模式相对而言是各大模式中较为简单的,但是单例模式也是较为常用并且很有用的模式。在JS中尤为突出(每个对象字面量都可以看做是一个单例么~)。

    记住,是否严格的只需要一个实例对象的类(虽然JS没有类的概念),那么就要考虑使用单例模式。

    使用数据缓存来存储该单例,用作判断单例是否已经生成,是单例模式主要的实现思路。

以上是关于js设计模式——单例模式的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript设计模式-7.单例模式

javascript设计模式学习——单例模式

[转]js设计模式-单例模式

Javascript 设计模式 单例 http://blog.csdn.net/lmj623565791/article/details/30490955/

[JavaScript设计模式]惰性单例模式

js设计模式学习 --- 单例模式1