前端设计模式应用--单例模式

104 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天

介绍

  • 单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例)

这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

使用场景

  • 要求生产唯一序列号。

  • WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。

  • 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

  • 在ES6中,import 引入的模块是会自动执行的,但是有的时候我们可能会重复引入一个模块,但是这个时候不会执行多次,因为import就用到了SIngleton模式。

应用实践

闭包方法实现

 var SingleInstance = (function () {
     let instance
     // 获取当前实例
     let getInstance = function (name) {
         this.name = name
     }
     // 返回一个创建单例的函数
     return function (name) {
         return instance || (instance = new getInstance(name))     }
 })()

//创建
const item1 = SingleInstance('test')
const item2 = SingleInstance('test')

console.log(item1===item2) // true,是同一个实例

原型方法实现

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

//获取实例
const item1 = SingleInstance.getInstance('test');
const item2 = SingleInstance.getInstance('test');console.log(item1===item2) // true,是同一个实例

类方式实现

class SingleInstance {    constructor(name) {
        this.name = name
        this.instance = null
    }
    static getInstance(name) {
        if (!this.instance) {
            this.instance = new Single(name)
        }
        return this.instance
    }
}
//获取实例
const item1 = SingleInstance.getInstance('test')
const item2 = SingleInstance.getInstance('test')

console.log(item1===item2) // true,是同一个实例

项目应用

创建一个唯一的dialog弹窗

html部分

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<button id="btn1">点我创建新窗口</button>
		<button id="hide">点我隐藏</button>
	</body>
</html>

js部分

const btn1 = document.querySelector('#btn1');
const createWindow = (() => {
    let div = null;
    return (words) => {
        if (!div) {
            div = document.createElement('div');
            div.innerHTML = words 
            div.className = 'common-box';
            div.style.display = 'none';
            document.body.appendChild(div);
        }

        return div;
    }
})();
btn1.addEventListener('click', () => {
    let box = createWindow('content');
    box.style.display = 'block';
}, false);
//隐藏
document.querySelector('#hide').addEventListener('click', () => {
    document.querySelector('.common-box').style.display = 'none';
}, false);

汇总

有点特性

  • 在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象都访问一个实例。

  • 由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。

  • 避免对共享资源的多重占用。

  • 系统中需要一个唯一的对象去控制、管理和分享系统的状态,或者执行某一个特定的任务又或是实现某一个具体的功能。在我们的前端开发中,最常见的就是应用的状态管理对象,比如 Vuex和 Redux。

  • 全局只有一个实例,提供统一的访问与修改,保证状态功能的一致性。

缺点弊端

  • 由于单利模式中没有抽象层,因此单例类的扩展有很大的困难

  • 单例类的职责过重,在一定程度上违背了单一职责原则

  • 不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误。

  • 单例模式的滥用会造成跟全局变量一样的一些问题。比如会增加代码的耦合性,因为单例模式全局都是可以访问到的,那么我们就很有可能在很多个地方使用这个唯一的对象

  • 单例模式隐藏了它所需要的依赖。它的依赖封装在内部,对于外部的使用者来说它是一个黑盒。使用者并不知道初始化这个单例需要那些依赖。