JavaScript实现设计模式中的单例模式的一些技巧总结
来源: 阅读:721 次 日期:2016-06-27 15:57:54
温馨提示: 小编为您整理了“JavaScript实现设计模式中的单例模式的一些技巧总结”,方便广大网友查阅!

单例模式是JavaScript项目中最常用的设计模式之一,下面罗列了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.

上面的方法似乎没什么问题,但通过下面的测试,可以发现问题

//向原型添加属性 

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.');

   }

  }

 }

})();

更多信息请查看网络编程
由于各方面情况的不断调整与变化, 提供的所有考试信息和咨询回复仅供参考,敬请考生以权威部门公布的正式信息和咨询为准!

2025国考·省考课程试听报名

  • 报班类型
  • 姓名
  • 手机号
  • 验证码
关于我们 | 联系我们 | 人才招聘 | 网站声明 | 网站帮助 | 非正式的简要咨询 | 简要咨询须知 | 加入群交流 | 手机站点 | 投诉建议
工业和信息化部备案号:滇ICP备2023014141号-1 云南省教育厅备案号:云教ICP备0901021 滇公网安备53010202001879号 人力资源服务许可证:(云)人服证字(2023)第0102001523号
云南网警备案专用图标
联系电话:0871-65317125(9:00—18:00) 获取招聘考试信息及咨询关注公众号:
咨询QQ:526150442(9:00—18:00)版权所有:
云南网警报警专用图标
Baidu
map