你真的了解单例模式吗?JS设计模式之单例模式

定义:一个类只有一个实例,即使多次实例化该类,也只会返回第一次实例化后的对象。
const 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;
}


let s1 = SingleTon.getInstance('s1');
let s2 = SingleTon.getInstance('s2');
console.log(s1 === s2); //true
//s1 = SingleTon { name: 's1', instance: null }
//s2 = SingleTon { name: 's1', instance: null }

使用闭包改进后的写法

const SingleTon = function(){}

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

let s1 = SingleTon.getInstance();
let s2 = SingleTon.getInstance();
console.log(s1 == s2) //true
//s1 == s2 == SingleTon {}

单例模式用途

如果一个类负责连接数据库的线程池、日志记录逻辑等等,此时需要单例模式来保证对象不被重复创建,以达到降低开销的目的。
对于频繁创建,销毁对象,单例模式实现了对象统一,内存只占有一个对象内存分量,在频繁创建中,大大的节约了系统内存。

实例演示

1、登录弹框

常见做法: 实现弹框的一种做法是先创建好弹框, 然后使之隐藏, 这样子的话会浪费部分不必要的 DOM 开销, 我们可以在需要弹框的时候再进行创建, 同时结合单例模式实现只有一个实例, 从而节省部分 DOM 开销。下列为登入框部分代码:

let createLoginBox= function() {
  let div = document.createElement('div')
  div.innerHTML = '登入弹框'
  div.style.display = 'none'
  document.body.appendChild(div)
  return div;
}

使单例模式和创建弹框代码解耦

let getSingle = function(fn) {
  let result;
  return function() {
    return result || (result = fn.apply(this, arguments));
  }
}

let createSingleLoginBox = getSingle(createLoginBox)

document.getElementById('loginBtn').onclick = function() {
     let div = createSingleLoginBox();
     console.log(div)
}

ElemnetUI2.x就用到了单例模式来重用遮罩

import Vue from 'vue'
import loadingVue from './loading.vue'

const LoadingConstructor = Vue.extend(loadingVue)

let fullscreenLoading

const Loading = (options = {}) => {
   if (options.fullscreen && fullscreenLoading) {
       return fullscreenLoading
   }

   let instance = new LoadingConstructor({
       el: document.createElement('div'),
       data: options
   })

   if (options.fullscreen) {
       fullscreenLoading = instance
   }
   return instance
}

export default Loading

这里的单例是 fullscreenLoading,是存放在闭包中的,如果用户传的 options 的 fullscreen 为 true 且已经创建了单例的情况下则回直接返回之前创建的单例,如果之前没有创建过,则创建单例并赋值给闭包中的 fullscreenLoading 后返回新创建的单例实例。
这是一个典型的单例模式的应用,通过复用之前创建的全屏蒙层单例,不仅减少了实例化过程,而且避免了蒙层叠加蒙层出现的底色变深的情况。

2、管理模块

.我们在开发中会写许多方法,这里罗列2个做个样子,代码如下

function getId(){
    var args = arguments;
    if(args.length > 1){
        throw new Error('只允许接收一个参数');
    }else{
        return document.getElementById(args[0]);
    }
}

function setHtml(id,text){
    document.getElementById(id).innerHTML = text;
}

改进如下

var myFun = {
    getId : function(){
        var args = arguments;
        if(args.length > 1){
            throw new Error('只允许接收一个参数');
        }else{
            return document.getElementById(args[0]);
        }
    },
    setHtml : function(id,text){
        this.getId(id).innerHTML = text;
    }
}

通过上面的方法,我们解决了2个问题,一个全局变量只有一个,我们可以通过.的形式来获取对应的方法;其次各个方法之间我们可以很方便的用this来进行调用,比起call要直观舒服好多。并且在使用中为了更加好的保护我们的变量,通常比较合理的代码如下所示:

(function(window){
    var myFun = {
        getId : function(){
            var args = arguments;
            if(args.length > 1){
                throw new Error('只允许接收一个参数');
            }else{
                return document.getElementById(args[0]);
            }
        },
        setHtml : function(id,text){
            this.getId(id).innerHTML = text;
        }
    }   
    window.myFun = myFun;
})(window)

我们用一个匿名函数来包裹代码,把全局污染变到最小化。当然单例模式我们更灵活的使用,例如{key1:{},key2:{}}这种嵌套结构来处理逻辑。

单例模式的优缺点

简单分析一下它的优点:

1、单例模式在创建后在内存中只存在一个实例,节约了内存开支和实例化时的性能开支,特别是需要重复使用一个创建开销比较大的类时,比起实例不断地销毁和重新实例化,单例能节约更多资源,比如数据库连接;
2、单例模式可以解决对资源的多重占用,比如写文件操作时,因为只有一个实例,可以避免对一个文件进行同时操作;
3、只使用一个实例,也可以减小垃圾回收机制 GC(Garbage Collecation) 的压力,表现在浏览器中就是系统卡顿减少,操作更流畅,CPU 资源占用更少;
单例模式也是有缺点的
1、单例模式对扩展不友好,一般不容易扩展,因为单例模式一般自行实例化,没有接口;
2、与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化;

单例模式的使用场景

那我们应该在什么场景下使用单例模式呢:
当一个类的实例化过程消耗的资源过多,可以使用单例模式来避免性能浪费;
当项目中需要一个公共的状态,那么需要使用单例模式来保证访问一致性;

你可能感兴趣的:(你真的了解单例模式吗?JS设计模式之单例模式)