单例模式

定义: 保证一个类只有一个实例,并提供一个访问它的全局访问点。

var single = function(name){
    this.name = name;
    this.instance = null;
}
single.prototype.getName = function(){
    alert(this.name)
}
single.getInstance = function(name){
    if(!this.instance){
        this.instance = new single(name)
    }
    return this.instance
}
var a = new single.getInstance('ffff');
var a2 = new single.getInstance('zzzz');
// a == a2  => true

或者

var single = function(name){
    this.name = name;
}
single.prototype.getName = function(){
    console.log(this.name)
}
single.getInstance = (function(){
    var instance = null;
    return function(name){
        if(!instance){
            instance = new single(name)
        }
        return isntance
    }
})()
  • 使用single类时,我们必须知道它是一个单例类,跟以往new出一个新对象的方式不同,这里要使用single.getInstance来获取对象。

透明的单例

var createDiv = (function(){
    var instance = null;
    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
})()
  • 这个透明类的单例,我们使用了自执行函数和闭包,并且让这个匿名函数返回真正的single构造方法,增加了代码的复杂度,读起来需要一定的时间来理解

  • 构造函数的作用,实际上做了两件事,创建对象和执行初始化init方法,第二保证了只有一个对象

用代理来实现单例模式

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 proxySingle = (function(){
    var instance;
    return function(html){
        if(!instance){
            instance = new createDiv(html)
        }
        return instance
    }
})()
  • 通过代理模式完成的代码,我把管理单例的逻辑移到了proxySingle里,这样createDiv就变成了一个普通的类

这个例子是缓存代理的应用之一。

惰性单例

  • 惰性单例指的是在需要的时候才创建对象实例。

这种技术在实际开发中非常有用。

在网站首页,有个登陆按钮,点击登录 弹出登录框。很明显这个登录框在页面中是唯一的,不会也不可能出现两个登陆框。

首先我们可以页面加载完成时就加载这个登录框,此时这个登录框出于隐藏状态。
但是如果我进入页面只是想看看,不想进行一些需要登陆的业务操作,此时登陆框的dom节点存在是没有意义的,是一种浪费。
此时,单例模式就更实用了

//dom
<button id="login"></button>
//js
var createLogin = (function(){
    var div;
    return function(){
        if(!div){
        div= document.createElement('div');
        div.innerHTML = "i'm login form";
        div.display.style="none";
        document.body.appendChild(div);
        return div
        }
    }
    })()
document.getElementById('login').onClick = function(){
    var loginDom = createLogin();
    loginDom.display.style ='block'
}

通用惰性单例

我们参考上边的代码,如果需要在创建一个iframe单例,或者其他标签,难道需要把上边的函数重新copy一份修改.单例的逻辑可以抽象出来,并且始终不变的,可以理解为

var instance;
if(!instance){
   instance = xxx操作
}
return instance

我们将管理单例的逻辑抽象封装起来getInstance

var getInstance = function(fn){
    var instance;
    return function(){
        return instance || (result = fn.apply(this,arguments))
    }
}
//创建登陆
var createLogin = getInstance(function(){
    var div = document.createElement('div');
    div.innerHTML = '我是的登陆';
    div.style.display = 'none';
    document.body.appenChild(div);
    return div
})
//
$('#login').on('click',function(){
    var login = createLogin();
    $(login).show()
})
//创建script
var createScript = getInstance(function(){
    var script = document.createElement('script');
    document.appendChild(script)
    return script
})
$('#loadScript').on('click',function(){
    var script = createScript();
    $(script).show();
})

将创建实例对象的职责和管理单例的职责分别放置在两个方法里,这两个方法独立,不会互相影响。


最普通的一个
301 声望41 粉丝

永远不要做你擅长的事。