You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
functionTest(name){varinstance=this;this.name=name;Test=function(){returninstance;}}Test.prototype.hi='hi';vara=newTest();Test.prototype.hello='hello';varb=newTest();console.log(a.hi,b.hi);// hi hiconsole.log(a.hello,b.hello)// undefined undefined
单例构造函数
也就是说,我们希望通过构造函数创建的所有的对象实例完全相等。
有的人可能会问,每一次的new操作,构造函数都会隐式地创建一个this对象,这样内存里不是还是会有不止一个对象吗?是的,但是垃圾回收机制会帮我们立刻释放掉那些后面没有再被引用的值,所以到最后内存里还是只有一个对象。
接下来,我们来想如何实现这样一个构造函数:
Test里面应该怎么实现呢?
我们知道对象是引用值,要完全相等,必须是地址相同,即new Test()返回的this对象始终是同一个。
于是我们想到可以把这个this对象保存在全局,但是这样并不好,因为任何人都可以修改该全局变量。
那么把this对象保存到构造函数的静态属性中呢?静态属性同样是公开可访问的属性,外部可以修改,并不符合开闭原则。
利用闭包,重写构造函数。代价是带来了额外的闭包开销。
这样确实使得a和b完全相等,但是这样写好不好呢? 接着看下面代码:
首先我在创建实例a之前,往Test的原型上添加了hi属性。然后,在创建实例b之前,往Test的原型上添加了hello属性。结果a和b都能找到hi,这是当然的也是我们所期待的,但都不能找到hello,这就不是我们所期待的了。这是为什么呢?
显而易见的是,前后两Test.prototype.hi = 'hi'和Test.prototype.hello = 'hello'这两个Test并不是同一个,后者指向的是function () {return instance;}。
第一次生成的this对象,也就是instance,他的__proto__属性指向了原来的Test的prototype,hi属性也是添加到这个上面的,而且后期new Test()返回的都是这个instance对象,所以a和b都能够找到hi。
而第二次是往新的Test的prototype上添加hello属性,而instance的__proto__始终指向原来的Test的prototype,所以都找不到hello属性。
第一种写法
知道了这个以后,我们就可以通过把新的Test的prototype指向原来的Test的Prototype来解决这个问题。
第二种写法
此外,我们还可以通过立即执行函数,产生私有变量instance,再返回一个函数作为构造函数,那么,无论什么时候往Test的prototype身上添加属性,都是添到这个返回的构造函数里。
将构造函数变成单例构造函数
以上只是实现了一个可以产生单例的构造函数。假如有很多个构造函数需要修改成单例的形式,那我们是不是要修改全部的构造函数呢 ?我们更希望提取出一个公共方法,可以把普通的构造函数转成单例构造函数,这就是我们接下来要解决的问题。
首先我们来看一个场景:点击按钮,然后弹窗(动态创建一个div,并显示)。
我们点击多少次,他就创建多少个div,这明显不是我们想要的。我们希望不管点多少次,他出现的始终是同一个div。
这时我们就可以运用单例的思想,把这个CreateAlert改成只有在第一次执行的时候才创建div,其他情况直接引用第一次创建好的div。
这里运用的是立即执行函数的写法。细心的网友可能会问,你这个不是构造函数啊?然而我把这里var oDiv = singleAlret('hello');改成var oDiv = new singleAlret('hello');不就是了吗,而且实现效果都是一样的,无伤大雅。
这样,我们无论点击多少次按钮,他始终就只有一个div了:
假如这时又需要出现一个单例的构造函数,例如singleIframe什么的,如果不提取公共方法,我们又要再实现这个单例构造函数,比较麻烦。
因此为了提高复用性,我们需要实现一个getSingle方法,把普通的函数改造成单例的函数并返回。
那么该如何写getSingle这个方法呢?很简单,我们只需要保存第一次函数的返回值,第二次开始直接返回这个返回值即可:
这样下来,通过改造的到的singleAleat,再尝试一下点击多次按钮,发现只存在一个div。成功,了吗?
好的,新的风暴又出现了。以上这个getStyle确实可以把普通的函数改造成单例模式的函数,但并没有把普通的构造函数改造成单例的形式。看下面:
你会发现 a 和 b 并不绝对相等了,而且成员属性都不一样,简直本末倒置。可恶,怎么会这样?
因为前面我们并没有考虑到 new 的情况。
上面的例子在 var a = new singleTest('xixi')的时候,singleTest发生了下面的变化:
然后 var b = new singleTest() 的时候,又隐式创建了一个this对象,因为result不绝对等于undefined,所以直接返回这个this对象,它是一个空对象。
好的,破案了。那么我们要怎么结案呢?
关键就是把new时候隐式创建的this对象作为result保存下来,那么如果判断有没有new呢?
我们知道new时,隐式创建的this对象的__proto__指向当前函数的prototype;而没有new时,this表示的是window。于是,我们就可以利用这一点判断是否有new 了。
这样,我们就完美地提取出一个公有方法,将普通的函数转为单例的形式了。
再来执行一下下面的代码:
完美!
The text was updated successfully, but these errors were encountered: