zoukankan      html  css  js  c++  java
  • javascript单例模式的理解

    javascript单例模式的理解

    阅读目录

    理解单例模式

    单例模式的含义是: 保证一个类只有一个实例,并提供一个访问它的全局访问点。实现的方法是:使用一个变量来标志当前是否已经为某个类创建过对象,如果创建了,则在下一次获取该类的实例时,直接返回之前创建的对象,否则就创建一个对象。这就确保了一个类只有一个实例对象。

    比如如下代码是一个简单的单例模式代码实例:

    var Singleton = function(name){
        this.name = name;
        // 使用instance 该标志来判断是否创建了一个实例
        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;
    }

    现在我们可以来使用下,初始化下,如下代码:

    var a = Singleton.getInstance("aa");
    var b = Singleton.getInstance("bbb");
    console.log(a);
    console.log(b);

    打印如下:

    继续如下测试:

    console.log(a === b);  // true
    
    a.getName();  // aa
    
    b.getName();  // aa
    
    a.test = "test";
    
    console.log(b.test); // test

    如上代码测试,可以看到,先是实例化一次,传aa给name参数,保存到a变量中,第二次再次调用getIstance方法,由于实例已经存在,所以使用之前第一次创建过的对象,因此 a ===b 为true,a.getName()和b.getName()值打印都为aa;

    我们还可以像如下方式来编写代码:

    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;
        }
    })();

    使用代理实现单例模式

    比如我现在想在页面上创建一个div元素,如下使用代理实现单例模式的代码:

    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("aa");
    var b = new ProxySingletonCreateDiv("bbb");
    console.log(a === b); // true

    如上代码:我们把负责管理单例的逻辑移到了ProxySingletonCreateDiv 函数中,CreateDiv函数就是一个普通的函数,就是只是负责创建div的方法,那么具体的管理单例的逻辑交给ProxySingletonCreateDiv函数;

    理解惰性单例

    惰性单例的含义是:在需要的时候才创建对象实例,而前面我们讲的是页面加载完的时候就创建实例;比如我们在页面上一个弹出窗口的div,还有许多其他的显示元素,如果有些用户不点击那个弹窗的话,那么在页面初始化的时候多创建了一些dom节点,如果我们使用惰性单例的话,我们就可以在用户需要的时候才去创建dom节点;

    我们首先来看看在页面加载完成的时候去创建div弹窗。这个弹窗一开始是隐藏的,当用户点击某个按钮的时候,这个弹窗才显示;代码如下:

    <button id="btn">请点击我</button>

    var CreateDiv = (function(){
        var div = document.createElement('div');
        div.innerHTML = "我是弹窗测试";
        div.style.display = "none";
        document.body.appendChild(div);
        return div;
    })();
    document.getElementById("btn").onclick = function(){
        CreateDiv.style.display = "block";    
    };
    惰性代码如下所示:
    var CreateDiv = function(){
        var div = document.createElement('div');
        div.innerHTML = "我是弹窗测试";
        div.style.display = "none";
        document.body.appendChild(div);
        return div;
    };
    document.getElementById("btn").onclick = function(){
        var createDiv = CreateDiv();
        createDiv.style.display = "block";    
    };

    如上代码,我们点击按钮的时候,才去创建div元素,但是每次点击的时候,我们都得创建元素,这样也不合理的。但是如上代码,我们可以使用一个变量来判断是否已经创建过div弹窗;如下所示:

    var CreateDiv = (function(){
        var div;
        return function(){
            if(!div) {
                div = document.createElement('div');
                div.innerHTML = "我是弹窗测试";
                div.style.display = "none";
                document.body.appendChild(div);
            }
            return div;
        }
    })();
    document.getElementById("btn").onclick = function(){
        var createDiv = CreateDiv();
        createDiv.style.display = "block";    
    };

    编写通用的惰性单例

    如上代码虽然完成了惰性单例,但是有些问题;

    1. 违反了单一职责原则;比如创建对象和管理单例的逻辑都放在CreateDiv对象内部;
    2. 没有把代码抽象出来,比如上面的是创建一个div元素,但是以后我想创建一个script元素或者一个iframe元素的话,那么我们还需要复制上面的代码重写下;

    比如如果我现在按照上面创建div的方法,现在我们需要再创建一个iframe元素的话,代码需要改成如下:

    var createIframe = (function(){
        var iframe;
        return function(){
            if(!iframe) {
                iframe = document.createElement('iframe');
                iframe.style.display = 'none';
                document.body.appendChild(iframe);
            }
            return iframe;
        }
    })();

    我们现在肯定在考虑如何把上面的代码公用出来,这样就可以实现抽象的代码,管理单例的逻辑代码其实可以抽象出来,这个逻辑是一样的,使用一个变量来标志是否创建过对象,如果是,在下次直接返回这个已经创建好的对象;

    我们可以把这些逻辑封装在getSingle函数内部,创建对象的方法fn被当成参数动态传入getSingle函数;如下代码:

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

    下面我们是使用getSingle创建一个div的方法如下:

    var CreateDiv = function(){
        var div = document.createElement('div');
        div.innerHTML = "我是弹窗测试";
        div.style.display = "none";
        document.body.appendChild(div);
        return div;
    };
    // 创建单例
    var createSingleDiv = getSingle(CreateDiv); 
    
    document.getElementById("btn").onclick = function(){
        // 调用单例方法
        var createDiv = createSingleDiv();
        createDiv.style.display = "block";    
    };

    比如现在我们需要创建一个iframe,那么代码如下:

    var createSingleIframe = getSingle(function(){
        var iframe = document.createElement('iframe');
        document.body.appendChild(iframe);
        return iframe;
    });
    document.getElementById("btn").onclick = function(){
        // 调用单例方法
        var createSingleIframe = createSingleIframe();
        createSingleIframe.src = "http://cnblogs.com";    
    };

    单例模式使用场景

    有一些对象我们只需要一个的情况下,比如弹窗这样的,全局缓存,游览器window对象等。

    单例模式只会创建一个实例,且仅有一个实例,比如我们一刚开始讲到的,

    var a = Singleton.getInstance("aa");
    
    var b = Singleton.getInstance("bbb");
    
    console.log(a === b);  // true
    
    a.getName();  // aa
    
    b.getName();  // aa

    我们明明第一次传的是aa,第二次传的参数是bbb,为什么都调用getName()方法后都打印出aa呢,这就是单例模式只创建一个实例的地方;

  • 相关阅读:
    vue同一页面中拥有两个表单时,验证问题
    Vue表单修饰符(lazy,number,trim)
    vue 去除前后空格trim
    git 命令大全
    localStorage 知识点
    vue-cli 打包后显示favicon.ico小图标
    Vue项目兼容IE浏览器
    System.Globalization.CalendarArgumentType.cs
    System.Globalization.Calendar.cs
    System.Globalization.CultureTypes.cs
  • 原文地址:https://www.cnblogs.com/tugenhua0707/p/4660236.html
Copyright © 2011-2022 走看看