JavaScript实现设计模式中的单例模式的一些技巧总
一、使用全局变量保存单例
这是最简单的实现方法
function Person(){ this.createTime=new Date(); } var instance=new Person(); function getInstance(){ return instance; }
加载该js时就创建一个Person对象,保存到instance全局变量中,每次使用都取这个对象。如果一次都没使用,那么创建的这个对象则浪费了,我们可以优化一下,
var instance function getInstance(){ if(!instance){ instance=new Person(); } return instance; }
这样,第一次使用时才创建对象。
这个方法的缺点是,instance是全局的变量,在多人合作或者开发周期比较长的情况下,很难保证instance不会被其它代码修改或覆盖,很可能到调用的时候,发现instance根本就不是Person对象。
我们考虑下使用闭包来封装起instance,使它不再是全局变量就可以解决这个问题了
二、闭包创建对象
var getInstance(){ var instance; return function(){ if(!instance){ instance=new Person(); } return instance; } }();
这样,instance就被封装起来了,不用担心被修改了。
现在通过getInstance()函数可以获得单例了。新的问题,如果我通过new Person()来创建对象,获得的还是多个对象,javascript又不可以像java一样把构造器私有化。那怎么样可以让多次new出来的对象都是一个实例呢?
三、构造函数的静态属性缓存实例
先看代码
function Person(){ //如果已经缓存了实例,则直接返回缓存的实例 if(typeof Person.instance==='object'){ return Person.instance; } this.createTime=new Date(); //缓存实例 Person.instance=this; return this; }
从代码可以看到,第一次new时,if的条件返回false,会往下走,初始化对象,然后保存对象到Person.instance这个静态属性中。
第二次new 时,if的条件返回true,直接返回Person.instance,不会再往下运行初始化的代码。所以不管new几次,返回的都是第一次创建的对象。
这个方法的缺点和方法一的缺点一样,Person.instance也是公开属性,有可能会被修改。
我们参考方法二,使用闭包来封装一个,也许就能解决该问题了
四、重写构造函数
这个方法要使用闭包,但不能像方法二那么简单,我们需要重写构造函数。
function Person(){ //缓存实例 var instance=this; this.createTime=new Date(); //重写构造函数 Person=function(){ return instance; } }
第一次new 时,调用原始构造函数先缓存该实例,然后再初始化,,重写该构造函数。以后再new 时,永远调用不到原始的构造函数了,只能调用到重写后的构造函数,而这个函数总是返回缓存的instance.
上面的方法似乎没什么问题,但通过狼蚁网站SEO优化的测试,可以发现问题
//向原型添加属性 Person.prototype.prop1=true; var p1=new Person(); //在创建初始化对象后,向该原型添加属性 Person.prototype.prop2=true; var p2=new Person(); //开始测试 console.log(p1.prop1);//结果为true console.log(p2.prop1);//结果为true console.log(p1.prop2);//结果为undefined console.log(p2.prop2);//结果为undefined console.log(p1.constructor===Person);//结果为false console.log(p2.constructor===Person);//结果为false
我们预期中的结果,应该是全都是true。
分析一下上述测试代码
Person.prototype.prop1=true;是在原始构造函数的原型下增加了prop1这个属性,并赋值
而在执行 var p1=new Person();之后,Person这个构造函数已经被重写了
所以Person.prototype.prop2=true;是在新的原型下增加prop2这个属性
var p2=new Person(); p2和p1实际上是同一个对象,即原始构造函数创建的对象
所以p1 p2都有prop1这个属性,而没有prop2这个属性
同样的,p1 p2的constructor指向的也是原始的构造函数,而Person此时已不是原来那个函数了
为了能按预期的结果那样运行,可以通过一些修改来实现
function Person(){ //缓存实例 var instance=this; //重写构造函数 Person=function(){ return instance; } //保留原型属性 Person.prototype=this; //实例 instance=new Person(); //重置构造函数引用 instance.constructor=Person; //其他初始化 instance.createTime=new Date(); return instance; }
再运行前面的测试代码,结果都是true了。
五、惰性加载
在大型或复杂的项目中,起到了优化的作用那些开销较大却很少用到的组件可以被包装到惰性加载单例中,示例程序
/ Singleton with Private Members, step 3. / MyNamespace.Singleton = (function() { // Private members. var privateAttribute1 = false; var privateAttribute2 = [1, 2, 3]; function privateMethod1() { ... } function privateMethod2(args) { ... } return { // Public members. publicAttribute1: true, publicAttribute2: 10, publicMethod1: function() { ... }, publicMethod2: function(args) { ... } }; })(); / General skeleton for a lazy loading singleton, step 1. / MyNamespace.Singleton = (function() { function constructor() { // All of the normal singleton code goes here. // Private members. var privateAttribute1 = false; var privateAttribute2 = [1, 2, 3]; function privateMethod1() { ... } function privateMethod2(args) { ... } return { // Public members. publicAttribute1: true, publicAttribute2: 10, publicMethod1: function() { ... }, publicMethod2: function(args) { ... } } } })(); / General skeleton for a lazy loading singleton, step 2. / MyNamespace.Singleton = (function() { function constructor() { // All of the normal singleton code goes here. ... } return { getInstance: function() { // Control code goes here. } } })(); / General skeleton for a lazy loading singleton, step 3. / MyNamespace.Singleton = (function() { var uniqueInstance; // Private attribute that holds the single instance. function constructor() { // All of the normal singleton code goes here. ... } return { getInstance: function() { if(!uniqueInstance) { // Instantiate only if the instance doesn't exist. uniqueInstance = constructor(); } return uniqueInstance; } } })();
六、使用分支单例
针对特定环境的代码可以被包装到分支型单例中,示例程序
/ SimpleXhrFactory singleton, step 1. / var SimpleXhrFactory = (function() { // The three branches. var standard = { createXhrObject: function() { return new XMLHttpRequest(); } }; var activeXNew = { createXhrObject: function() { return new ActiveXObject('Msxml2.XMLHTTP'); } }; var activeXOld = { createXhrObject: function() { return new ActiveXObject('Microsoft.XMLHTTP'); } }; })(); / SimpleXhrFactory singleton, step 2. / var SimpleXhrFactory = (function() { // The three branches. var standard = { createXhrObject: function() { return new XMLHttpRequest(); } }; var activeXNew = { createXhrObject: function() { return new ActiveXObject('Msxml2.XMLHTTP'); } }; var activeXOld = { createXhrObject: function() { return new ActiveXObject('Microsoft.XMLHTTP'); } }; // To assign the branch, try each method; return whatever doesn't fail. var testObject; try { testObject = standard.createXhrObject(); return standard; // Return this if no error was thrown. } catch(e) { try { testObject = activeXNew.createXhrObject(); return activeXNew; // Return this if no error was thrown. } catch(e) { try { testObject = activeXOld.createXhrObject(); return activeXOld; // Return this if no error was thrown. } catch(e) { throw new Error('No XHR object found in this environment.'); } } } })();
编程语言
- 如何快速学会编程 如何快速学会ug编程
- 免费学编程的app 推荐12个免费学编程的好网站
- 电脑怎么编程:电脑怎么编程网咯游戏菜单图标
- 如何写代码新手教学 如何写代码新手教学手机
- 基础编程入门教程视频 基础编程入门教程视频华
- 编程演示:编程演示浦丰投针过程
- 乐高编程加盟 乐高积木编程加盟
- 跟我学plc编程 plc编程自学入门视频教程
- ug编程成航林总 ug编程实战视频
- 孩子学编程的好处和坏处
- 初学者学编程该从哪里开始 新手学编程从哪里入
- 慢走丝编程 慢走丝编程难学吗
- 国内十强少儿编程机构 中国少儿编程机构十强有
- 成人计算机速成培训班 成人计算机速成培训班办
- 孩子学编程网上课程哪家好 儿童学编程比较好的
- 代码编程教学入门软件 代码编程教程