你真的了解单例模式吗?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、与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化;

单例模式的使用场景

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容