JavaScript实现设计模式中的单例模式的一些技巧总结

作者:luojianjava 时间:2024-05-02 16:21:11 

一、使用全局变量保存单例

这是最简单的实现方法


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

})();
标签:JavaScript,设计模式,单例模式
0
投稿

猜你喜欢

  • Python中用于去除空格的三个函数的使用小结

    2022-02-07 23:43:45
  • 检测SQL Server是否有特洛伊木马

    2009-02-24 15:19:00
  • python 接口测试response返回数据对比的方法

    2023-10-12 14:01:31
  • python的sorted函数及使用解析

    2022-03-02 05:52:21
  • 10行Python代码助你整理杂乱无章的文件

    2021-03-08 21:11:12
  • 如何基于Python制作有道翻译小工具

    2023-10-01 21:00:58
  • 使用OpenCV获取图片连通域数量,并用不同颜色标记函

    2023-10-17 19:58:05
  • innodb_index_stats导入备份数据时报错表主键冲突的解决方法

    2024-01-19 19:33:51
  • 对python读取zip压缩文件里面的csv数据实例详解

    2022-04-14 10:48:57
  • 详解python基础中的for循环

    2021-04-23 07:42:11
  • Pandas直接读取sql脚本的方法

    2022-08-16 20:16:34
  • 解决python通过cx_Oracle模块连接Oracle乱码的问题

    2023-04-30 23:25:45
  • 快速理解MySQL中主键与外键的实例教程

    2024-01-26 16:16:57
  • Go语言程序开发gRPC服务

    2024-04-23 09:38:02
  • 利用Python yagmail三行代码实现发送邮件

    2021-10-16 08:39:04
  • asp多关键词查询方案

    2008-05-09 12:24:00
  • opencv绘制矩形和圆的实现

    2021-09-24 15:27:22
  • 如何使div在任何分辩率的情况下居中

    2007-08-13 09:10:00
  • LRUCache的实现原理及利用python实现的方法

    2022-06-26 06:51:51
  • PyTorch数据读取的实现示例

    2022-01-31 04:15:48
  • asp之家 网络编程 m.aspxhome.com