美文网首页我爱编程
JavaScript设计模式二(单例模式)

JavaScript设计模式二(单例模式)

作者: moyi_gg | 来源:发表于2018-04-16 14:21 被阅读0次

    JavaScript设计模式二(单例模式)

    这边文章主要是JavaScript中的单例模式
    定义:

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

    其实我们的日常开发中或多或少的用到了单例模式的方法。例如我们做Electron开发的过程中,点击一个按钮创建了一个窗口,后续点击的时候,如果窗口已经存在了就focus窗口,否则创建;或者我们经常会创建一个定时任务,同时把定时任务赋值给一个变量,如果变量不存在就创建,否则不创建。但是大多数情况我们都是利用的是一个变量来控制的。接下来我们看看代码的实现

    实现单例模式

    其实上面的介绍我们已经说了实现单例模式的思路,就是通过一个变量来控制

    var 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;
    }
    
    //这种写法借助了this.instance,其实可以不需要
    
    
    var Singleton = function(name) {
        this.name=name;
    }
    Singleton.prototype.getName=function(){
        console.log(this.name);
    }
    Singleton.getInstance=(function(){
        var instance = null;
        return function(name) {
            if(!instance) {
                instance = new Singleton(name);
            }
            return instance;
        };
    })();
    

    使用方法:

    var a = Singleton.getInstance('a');
    var b = Singleton.getInstance('b');
    a===b
    

    这种方式实现了我们说的单例模式,但是有一个很明显的缺点,就是我们实例化的时候不是使用的new方法来实例化,而是用的getInstance方法,也就是说我们必须知道这个类是单例类,才能这样去用,这就增加了不透明性。

    透明的单例模式

    所谓的透明的单例模式,就是我们可以像正常的类那样去new一个单例类。

    var CreateDiv = (function(){
        var instance;
        var CreateDiv = function(html) {
            if (instance) {
                return instance;
            }
            this.html = html;
            this.init();
            return instance = this;
        };
        
        CreateDiv.prototype.init = function() {
            var div = document.createElement('div');
            div.innerHTML = this.html;
            document.body.appendChild('div');
        };
        
        return CreateDiv;
    })();
    
    var a = new CreateDiv('div');
    var b = new CreateDiv('div');
    
    a===b
    

    之所以能够通过new来创建一个单例类的实例,是因为CreateDiv
    的返回值是一个构造函数,这个构造函数做了两件事情

    • 创建对象和执行init方法
    • 保证只有一个对象

    我们可以想象如果需求变成了,我们需要CreateInput之类,是不是一直要修改这个类呢?

    代理实现单例模式

    利用代理就可以很好的解决上面的问题

    var CreateDiv = function(html) {
        this.html = html;
        this.init();
    }
    
    CreateDiv.prototype.init = function() {
       var div = document.createElement('div');
       div.innerHTML = this.html;
       document.body.appendChild('div');
    };
    
    var ProxySingletonCreateDiv = (function(){
        var instance;
        return function(html) {
            if (!instance) {
                instance = new CreateDiv(html);
            }
            return instance;
        }
    })();
    
    
    var a = new ProxySingletonCreateDiv('div');
    var b = new ProxySingletonCreateDiv('div');
    

    利用代理类,我们遵循了单一职责的原则,让代理类负责单例的逻辑,CreateDiv变成一个普通的创建html的类,两者结合达到单例模式的效果

    JavaScript中的单例模式

    JavaScript单例模式的核心是:

    确保只有一个实例,并提供全局访问

    与传统的面向对象语言不一样,JavaScript可以定义全局变量,而且我们通常认为全局变量就是一个单例,但是这种使用方式很容易造成命名空间的污染,针对这种问题有两种办法

    • 使用命名空间
      最简单的就是使用字面量常量:
    var namespace1 = {
        a: function(){},
        b: 'bbbb'
    }
    
    • 利用闭包封装变量
    
    var user = (function(){
        
        var _user = 'hahaha';
        return {
            setUserName: function(name) {
                _user = name;
            },
            getUserName: function(){
                console.log(_user);
            }
        }
    })();
    

    惰性单例

    定义:

    惰性单例是指需要时才创建的单例

    上面的那几种方法实际上就是惰性单例,但是那些事面向对象的,我们看看JavaScript中的惰性单例,看一段PC版的代码吧

    let historyWindow = null;
    ipc.on(cfg.CHANNEL.LOCAL.CHAT.SEARCH_HISTORY, function(event, arg) {
      if (!historyWindow) {
        historyWindow = new BrowserWindow({
          width: 621,
          height: 540,
          minWidth: 621,
          minHeight: 540,
          frame: false,
          show: false,
        });
        historyWindow.setAutoHideMenuBar(false);
        historyWindow.loadURL('file://' + __dirname + '/../../../client/views/history.html');
        if (process.env.NODE_ENV == 'dev') {
          historyWindow.webContents.openDevTools();
        }
        historyWindow.on('close', function() {
          historyWindow.webContents.closeDevTools();
        });
        historyWindow.on('closed', function() {
          glb.set(cfg.GLB.HISTORY_WINDOW, null);
          glb.remove(cfg.GLB.ADD_MEMBER_WINDOW);
          historyWindow = null;
        });
        historyWindow.webContents.on('did-finish-load', function() {
          glb.set(cfg.GLB.HISTORY_WINDOW, historyWindow);
          historyWindow.show();
          historyWindow.focus();
          historyWindow.webContents.send(cfg.CHANNEL.LOCAL.CHAT.OPEN_HISTORY_WINDOW_RECV, arg);
        });
      } else {
        historyWindow.webContents.send(cfg.CHANNEL.LOCAL.CHAT.OPEN_HISTORY_WINDOW_RECV, arg);
        if (historyWindow.isMinimized()) {
          historyWindow.show();
        } else {
          historyWindow.focus();
        }
      }
    });
    

    这段代码的逻辑是,点击历史消息的icon,创建一个历史消息的弹出框,后面如果继续点击,就把之前的弹出框focus。

    这里其实还有优化的空间,我们知道electron创建一个新的BrowserWindow是很慢的,所以我们创建一次之后,用户点击关闭,其实可以隐藏起来,并不是实际的关闭,这样当用户点击第二次的时候就省略了创建窗口的过程,直接渲染数据就可以。

    相关文章

      网友评论

        本文标题:JavaScript设计模式二(单例模式)

        本文链接:https://www.haomeiwen.com/subject/mecekftx.html