js单例模式

            window.onload = function() {
                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("sven1");
                var b=new ProxySingletonCreateDiv("sven2");
                alert(a===b);
            }
阅读 4.8k
5 个回答

这个匿名函数形成了一个闭包,单例对象instance会保存在这个闭包中,防止它不会被回收掉

首先按照LZ说的把自执行去掉改成普通Function的闭包

var ProxySingletonCreateDiv=(function(){
    var instance;
    return function(html){
        if(!instance){
            instance=new CreateDiv(html);
        }
        return instance;
    }
})();
   
var a=new ProxySingletonCreateDiv("sven1");
var b=new ProxySingletonCreateDiv("sven2");
alert(a===b); //这里输出false

好,false显然不能满足单例的要求,我们要把代码改成如下才能实现单例

var ProxySingletonCreateDiv=(function(){
var instance;
return function(html){
    if(!instance){
        instance=new CreateDiv(html);
    }
    return instance;
}
})();
var CreateDiv = new ProxySingletonCreateDiv();   
var a= CreateDiv ("sven1");
var b= CreateDiv ("sven2");
alert(a===b); //这回输出了true 实现了单例

所以这里就有个问题,不使用自执行的方式实现单例就显得不够优雅。再解释区别之前,我们先来说说,用new来初始化一个function和直接调用一个function的区别。来看看下面的例子。

function A(){
    this.user = {"name":"helloA"};
    return this.user.name;
}
function B(){
    this.user = {"name":"helloB"};
    return this.user;
}
function C(){
    this.user = {"name":"helloC"};;
    return function(){
        return this.user;
    };
}

var a = new A();
console.log(a); //打印出完整的a对象,由于return是个字符串,被直接无视了
var a1 = A();
console.log(a1); //打印出helloA,这个很正常
var b = new B();
console.log(b); //打印出user对象{"name","helloB"},return的值因为是个对象,没有被无视,这点和function A不同
var c = new C();
console.log(c); //打印出return的 anonymous function,也没有被无视,和function A 不同

通过上面的例子我们可以看出,使用new关键字初始化的function会返回this对象,而无视return的值,除非return的是一个对象,或者function。 而直接使用Function()方式则直接返回return的值。
好了,讲到这里一些朋友可能已经看出题主的问题关键之所在了,他不是在问闭包的问题,而是new关键字的问题。

var ProxySingletonCreateDiv=(function(){
    var instance;
    return function(html){
        if(!instance){
            instance=new CreateDiv(html);
        }
        return instance;
    }
})();

当我们使用自执行方式的时候其实 var ProxySingletonCreateDiv这个变量已经指向了这个自执行匿名函数的return值,也就是另外一个匿名函数

function(html){
    if(!instance){
        instance=new CreateDiv(html);
    }
    return instance;
}
即
var instance;
var ProxySingletonCreateDiv = function(html){
    if(!instance){
        instance=new CreateDiv(html);
    }
    return instance;
}

所以,再后面的2次new关键字的使用,其实你new的是上面这个return的匿名函数,由于这个匿名函数本身是return的是一个实例,所以你使用了new关键字也不会影响到它的返回(参见上面的function C()例子 )。因为instance这个变量是闭包的,可以保证自己的状态而不被回收,闭包这个就不用我多解释了,大家都懂得。

以上

其实清楚了闭包的特性就会明白为什么了。
当一个函数返回它内部定义的一个函数时,就产生了一个闭包,闭包包括了被返回的函数以及这个函数的定义环境
题主例子中的匿名函数自执行后,ProxySingletonCreateDiv匿名函数内的局部变量instance就是一个闭包,相当于instance并没有随着匿名函数自执行后而释放。
在还没有调用ProxySingletonCreateDiv时,变量instanceundefined
当第一次调用ProxySingletonCreateDiv时,由于if语句为true,变量instance被赋值为了CreateDiv的一个对象。
当第二次调用ProxySingletonCreateDiv时,由于if语句为false,变量instance不会再被赋值,还是第一次调用后的值。

代理是单例模式,,作用是只创建一次节点,避免多次创建节点。如 a b 跳过用户代理,就会创建出两个 DIV。

可以达到同样效果的办法是:

  1. 不用代理:把单例直接写在 onload 回调里,但题主之代码其实还有单一职责之意

  2. 不用单例:onload 里将其重新设为 null,也能避免重复创建的可能性。如下:

window.onload = function(){
      window.onload = null;
      // initial code
};

返回的函数里面的instance始终指向内部的那个变量==

其实prototype应该也算是单实例

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题