设计模式之单例模式

发布于:2024-04-29 ⋅ 阅读:(31) ⋅ 点赞:(0)

目录

定义

适用场景

非单例模式

单例模式实现

闭包方式实现

静态方法实现

场景举例


定义

单例模式是一种创建型设计模式,它保证一个类仅有一个实例,并提供一个访问它的全局访问点。

适用场景

“单例模式的特点,意图解决:维护一个全局实例对象。”

  • 引用第三方库(多次引用只会使用一个库引用,如 jQuery)
  • 弹窗(登录框,信息提升框)
  • 购物车 (一个用户只有一个购物车)
  • 全局态管理 store (Vuex / Redux)

项目中引入第三方库时,重复多次加载库文件时,全局只会实例化一个库对象,如 jQuerylodashmoment ..., 其实它们的实现理念也是单例模式应用的一种:

// 引入代码库 libs(库别名)
if (window.libs != null) {
  // 直接返回
  return window.libs;    
} else {
  // 初始化
  window.libs = '...';   
}

非单例模式

在实现单例模式前,先看一下正常对象实例化,是否符合单例模式,用构造函数或者 Class(本质上还是构造函数)来实现都可以,我们这里就直接使用构造函数做示例,实现如下代码,可以看出相同的构造函数 Singleton,经过两次创建的对象实例 s1、s2 并不相等,对象指向的内存地址不一致,不是同一个实例,不符合单例模式。

function Singleton() {}
const s1 = new Singleton();
const s2 = new Singleton();
console.log(s1); 
console.log(s2);
console.log(s1 === s2);
// {}
// {}
// false

单例模式实现

既然直接实例化不符合单例模式,那要怎么才能实现单例模式呢?要实现单例模式,需要构造函数具备判断自己是否已经创建过一个实例的能力。

闭包方式实现

不管执行多少次,返回的都是同一个实例,而闭包刚好就能满足我们的需求,创建一个立即执行函数,返回一个函数,而这个函数返回函数的内部变量 instance,也就是我们想要的唯一实例。我们实现了如下代码,进行了两次实例化,生成实例对象 s1、s2,根据输出可以判定 s1 与 s2 相等,即 s1,s2 指向同一块内存地址,是同一个实例,符合单例模式。

const Singleton = (function () {
    // 实例变量
    let instance = null;
    // 实例的构造函数
    function getInstance() {}
    return function () {
      // 判断是否已经 new 过1个实例
      if (!instance) {
        // 如果实例不存在,则先new一个实例
        instance = new getInstance();
      }
      // 未来不管执行多少次,都返回这个唯一实例
      return instance;
    };
})();
const s1 = Singleton();
const s2 = Singleton();
console.log(s1);
console.log(s2);
console.log(s1 === s2);
// getInstance {}
// getInstance {}
// true
静态方法实现

ES6 Class 的静态方法也能实现单例模式,原理是借助于静态属性和静态方法,Class 的本质是一个构造函数,存在 static 修饰符的属性称为静态属性,直接挂载在构造函数上,当前类未被销毁时,静态属性也不会被销毁,具有类似于闭包的缓存作用,可以用来存储实例,实现了如下代码,借助 Singleton.getInstance() 生成实例对象 s1、s2,根据输出可以判定 s1 与 s2 相等,即 s1,s2 指向同一块内存地址,是同一个实例。

class Singleton{

  static instance;

  show() {
    console.log('我是一个单例对象')
  }
  static getInstance() {
    // 判断是否已经new过1个实例
    if (!Singleton.instance) {
      // 若这个唯一的实例不存在,那么先创建它
      Singleton.instance = new Singleton()
    }
    // 如果这个唯一的实例已经存在,则直接返回
    return Singleton.instance
  }
}
const s1 = Singleton.getInstance()
const s2 = Singleton.getInstance()
console.log(s1 === s2); 
// true

实际上,基于 ES6 使用模块导入导出,也可以看作单例模式:

// 比如这是 SingletonExample.js
// 可以定义一些属性,方法等等,然后通过 export 导出,其他模块再分别导入

const array_state = [];

const methodTest = function() {
    console.log('这是测试方法')
}

export default {
    array_state, methodTest
}

场景举例

利用单例模式思想 来实现 登录框的 显示和隐藏

class LoginForm {
    constructor(){
        this.state = 'hide'
    }
    
    show(){
        if(this.state === 'show'){
            console.log('已经显示');
            return
        }
        this.state = 'show';
        console.log('登录框已显示')
    }
    
    hide(){
        if(this.state === 'hide'){
            console.log('已经隐藏');
            return
        }
        this.state = 'hide';
        console.log('登录框已隐藏')
    }
}

LoginForm.getInstance = (function(){
    let instance
    return function() {
        if(!instance){
            instance = new LoginForm();
        }
        return instance
    }
})

测试:

let login1 = LoginForm.getInstance();
login1.show(); //打印:登录框显示成功


let login2 = LoginForm.getInstance();
login2.hide(); //打印:登录框隐藏成功

console.log(login1 === login2)