JavaScript中的单例模式
单例模式
在JavaScript中,单例(Singleton)模式是最基本又最有用的模式之一。这种模式提供了一种将代码组织为一个逻辑单元的手段,这个逻辑单元中的代码可以通过单一的变量进行访问。确保单例对象只有一份实例,你就可以确信自己的所有代码使用的都是同样的全局资源。
单例类在JavaScript中用途广泛:
(1)可以用来划分命名空间,以减少网页中全局变量的数量;
(2)可以在一种名为分支的技术中用来封装浏览器之间的差异;
(3)可以借助于单例模式,将代码组织得更为一致,从而使代码更容易阅读和维护。
单例的基本结构
最基本的单例实际上是一个对象字面值,它将一批有一定关联的方法和属性组织在一起。例如如下JavaScript代码:
var Singleton = {
attribute1: true;
attribute2: 10 method1: function() { },
method2: function() { }
};
这些成员可以通过Singleton加圆点运算符来访问:
Singleton.attribute1 = false;
var total = Singleton. attribute2 + 5;
var result = Singleton.method1();
对象字面值只是用以创建单例的方法之一,后面介绍的那些方法所创建的单体看起来更像其他面向对象语言中的单例类。另外,并非所有对象字面值都是单体,如果它只是用来模仿关联数组或容纳数据的话,那显然不是单例。但如果它是用来组织一批相关方法和属性的话,那就可能是单例,其区别主要在于设计者的意图。
创建拥有私有成员的单例
使用下划线表示法
在单例对象内创建类的私有成员的最简单、最直截了当的方法是使用下划线表示法(在JavaScript业界,如果变量和方法是使用下划线,则表示该变量和方法是私有方法,只允许内部调用,第三方不应该去调用)。参考实例如下:
GiantCorp.DataParser = {
// 私有方法
_stripWhitespace: function(str) {
return str.replace(/\s+/, '');
},
_stringSplit: function(str, delimiter) {
return str.split(delimiter);
},
// 公用方法
stringToArray: function(str, delimiter, stripWS) {
if (stripWS) {
str = this._stripWhitespace(str);
} var outputArray = this._stringSplit(str, delimiter);
return outputArray;
}
};
在如上代码中,stringToArray方法中使用this访问单体中的其他方法,这是访问单体中其他成员或方法的最简便的方法。但这样做有一点风险,因为this并不一定指向GiantCorp.DataParser例如,如果把某个方法用作事件监听器,那么其中的this指向的是window对象,因此大多数JavaScript库都会为事件关联进行作用域校正,例如在这里使用GiantCorp.DataParser比使用this更为安全。
使用闭包
在单例对象中创建私有成员的第二种方法是借助闭包。因为单例只会被实例化一次,所以不必担心自己在构造函数中声明了多少成员。每个方法和属性都只会被创建一次,所以可以把它们声明在构造函数内部(因此也就位于同一个闭包中)。
使用闭包创建拥有私有成员的单例类的实例如下:
MyNamespace.Ssingleton = (function() {
// 私有成员
var privateAttribute1 = false;
var privateAttribute2 = [1, 2, 3]; function privateMethod1() { }
function privateMethod2() { } return {
// 公有成员
publicAttribute1: true;
publicAttribute2: 10, publicMethod1: function() { },
publicMethod2: function() { }
};
})();
这种单例模式又称为模块模式,指的是它可以把一批相关方法和属性组织为模块并起到划分命名空间的作用。 使用该种方式改造上面的实例,参考代码如下:
GiantCorp.DataParser = (function() {
var whiteSpaceRegex = /\s+/;
// 私有方法
function stripWhitespace(str) {
return str.replace(whiteSpaceRegex, '');
}
function stringSplit(str, delimiter) {
return str.split(delimiter);
},
return {
// 公用方法
stringToArray: function(str, delimiter, stripWS) {
if (stripWS) {
str = stripWhitespace(str);
} var outputArray = stringSplit(str, delimiter);
return outputArray;
}
};
})();
将私有成员放在闭包中可以确保其不会在单例对象之外被使用,因此开发人员可以自由的改变对象的实现细节,而不会殃及别人的代码。还可以使用这种办法对数据进行保护和封装。
在JavaScript中实现“懒汉式”单例模式
在如上的代码中,单例对象都是在脚本加载时被创建出来。对于资源密集型或配置开销甚大的单例,更合理的是使用“懒汉式”单例实现。这种实现方式的特别之处在于,对它们的访问必须借助于一个静态方法,例如调用单例类的getInstance()方法获得对象实例。
参考实现代码如下:
MyNamespace.Singleton = (function() {
// 定义一个私有的属性,该属性用于储存单例对象
var uniqueInstance;
function constructor() {
// 将单态操作放在这里
}
return {
getInstance: function() {
if (!uniqueInstance) {
uniqueInstance = constructor();
} return uniqueInstance;
}
}
})();
将一个单例转换为懒汉式加载方式后,必须对调用它的代码进行修改,例如之前调用:
MyNamespace.Singleton.publicMethod1();
应该改成如下代码:
MyNamespace.Singleton.getInstance().publicMethod1();
如果觉得命名空间名称太长,可以创建一个别名来简化它。
使用单例模式实现分支
分支是一种用来把浏览器之间的差异封装到运行期间进行设置的动态方法中的技术。例如,假设我们需要一个创建XHR对象的方法,这种XHR对象在大多数浏览器中是XMLHttpRequest对象的实例,但在IE早期版本中是某种ActiveX类的实例,这样一种方法通常会进行某种浏览器嗅探或对象探测。如果不用分支技术,那么每次调用这个方法时,所有这些浏览器嗅探代码都要再次运行。如果该方法调用频繁,将会严重影响效率。
要实现获取不同浏览器的XHR对象的功能,参考实现代码的实现步骤如下:
(1)判断有多少个分支(有3个),这些分支按其返回的XHR对象类型命名,这三个分支都有一个名为createXhrObject()的方法,该方法返回一个可以执行异步请求的新对象;
(2)根据条件将3个分支中某一分支的对象赋给那个变量,具体做法是逐一尝试XHR对象,直到遇到一个当前JavaScript环境所支持的对象为止。
参考代码如下所示:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>使用单例模式实现JavaScript中的分支</title>
<script type="text/javascript"> var SimpleXhrFactory = (function() {
// 三个分支
var standard = {
createXhrObject: function() {
alert("standard createXhrObject");
return new XMLHttpRequest();
}
};
var activeXNew = {
createXhrObject: function() {
alert("activeXNew createXhrObject");
return new ActiveXObject("Msxml2.XMLHTTP");
}
};
var activeXOld = {
createXhrObject: function() {
alert("activeXOld createXhrObject");
return new ActiveXObject("Microsoft.XMLHTTP");
}
};
// 为分支赋值
var testObject;
try {
testObject = standard.createXhrObject();
return standard;
} catch(e) {
try {
testObject = activeXNew.createXhrObject();
return activeXNew;
} catch(e) {
try {
testObject = activeXOld.createXhrObject();
return activeXOld;
} catch(e) {
throw new Error("在该浏览器环境中没有发现XHR对象.");
}
}
}
})();
SimpleXhrFactory.createXhrObject(); </script></head><body></body>
用了分支技术后,所有的那些特性嗅探代码都只会执行一次,而不是没生成一个对象执行一次。这种技术适用于任何只有在运行时才能确定具体实现的情况。
单例模式的优缺点
在JavaScript中使用单例模式的主要优点如下:
(1)对代码的组织作用:它将相关方法和属性组织在一个不会被多次实例话的单例中,可以使代码的调试和维护变得更轻松。描述性的命名空间还可以增强代码的自我说明性。将方法包裹在单例中,可以防止它们被其它程序员误改。
(2)单例模式的一些高级变体可以在开发周期的后期用于对脚本进行优化。
主要缺点如下:
(1)因为提供的是一种单点访问,所以它有可能导致模块间的强耦合。单体最好是留给定义命名空间和实现分支型方法这些用途,这些情况下,耦合不是什么问题;
(2)有时某些更高级的模式会更符合任务的需要。与“懒汉式”加载单例相比,虚拟代理能给予你对实例化方式更多的控制权。也可以用一个真正的对象工厂来取代分支型单例。
本文学习地址:http://www.108js.com/article/article5/50021.html?id=333
JavaScript中的单例模式的更多相关文章
- JavaScript设计模式,单例模式!
单例设计模式:保证一个类仅有一个实例,并且提供一个访问它的全局访问点.有些对象只需要一个,这时可用单例模式. 传统的单例模式 和new 创建对象的调用不一样 调用者要调用xxx.getInstance ...
- 再起航,我的学习笔记之JavaScript设计模式10(单例模式)
单例模式 单例模式(Singleton) : 又被称为单体模式,是只允许实例化一次的对象类.一个类有且仅有一个实例,并且自行实例化向整个系统提供. 命名空间 单例模式可能是JavaScript中我们最 ...
- 「设计模式」JavaScript - 设计模式之单例模式与场景实践
单例介绍 上次总结了设计模式中的module模式,可能没有真真正正的使用在场景中,发现效果并不好,想要使用起来却不那么得心应手, 所以这次我打算换一种方式~~从简单的场景中来看单例模式, 因为Java ...
- JavaScript 中常见设计模式整理
开发中,我们或多或少地接触了设计模式,但是很多时候不知道自己使用了哪种设计模式或者说该使用何种设计模式.本文意在梳理常见设计模式的特点,从而对它们有比较清晰的认知. JavaScript 中常见设计模 ...
- JavaScript中常见的十五种设计模式
在程序设计中有很多实用的设计模式,而其中大部分语言的实现都是基于“类”. 在JavaScript中并没有类这种概念,JS中的函数属于一等对象,在JS中定义一个对象非常简单(var obj = {}), ...
- JavaScript设计模式(单例模式)
单例模式是一种简单但非常实用的模式,特别是惰性单例技术,在合适的时候才创建对象,并且只创建唯一的一个.下面我们来逐步了解单例模式的用法. 一.简版单例模式: var Singleton = funct ...
- JavaScript设计模式之单例模式【惰性单例】
在提高开发水平,往中高级前端工程师中,利用设计模式是必不可少的一条道路.掌握设计模式的思想远远比硬套重要,因为设计模式是一种思想,不局限于开发语言.但实际上由于语言的特性不同,往往在实现的时候会有不少 ...
- javascript中的Array对象 —— 数组的合并、转换、迭代、排序、堆栈
Array 是javascript中经常用到的数据类型.javascript 的数组其他语言中数组的最大的区别是其每个数组项都可以保存任何类型的数据.本文主要讨论javascript中数组的声明.转换 ...
- javascript中的this与函数讲解
前言 javascript中没有块级作用域(es6以前),javascript中作用域分为函数作用域和全局作用域.并且,大家可以认为全局作用域其实就是Window函数的函数作用域,我们编写的js代码, ...
随机推荐
- Extjs 项目中常用的小技巧,也许你用得着(4)---Extjs 中的cookie设置
1.ExtJs设置cookie两种方式 其一:设置cookie如下 saveacct=isForm.getForm().findField('itemselector').getValue(); Ex ...
- EF只更新变化的字段
摘要 在使用EF的时候,由于表字段较多,所以在更新的时候,想要只更新变化的字段,有没有办法呢? 解决办法 代码片段 public async Task<int> UpdateAsync(T ...
- [日常] Go语言圣经-竞争条件习题
package main import( "fmt" "sync" ) var balance int func Deposit(amount int) { b ...
- Android-远程Service
http://blog.csdn.net/guolin_blog/article/details/9797169 http://www.jianshu.com/p/eeb2bd59853f 将一个普通 ...
- Android-加载图片避免OOM
http://blog.csdn.net/guolin_blog/article/details/9316683 高效加载大图片 我们在编写Android程序的时候经常要用到许多图片,不同图片总是会有 ...
- apicloud 自定义模块引用aar
apicloud 引入aar包,如果使用apicloud自定义模块的话,如果是一般的jar包,我们需要把jar down到本地然后添加到module的libs中就可以了(不要想着用远程地址了,既然用a ...
- JS中判断数据类型的几种方法
1⃣️首先我们来了解一下js中的数据类型 1.基本数据类型:Undefined.Null.Boolean.Number.String(值类型) 2.复杂数据类型:Object(引用类型) (值类型和引 ...
- 在ActiveReports页面报表中显示Google地图
有些报表需求中需要我们显示国家.城市等地址信息,在报表中添加地图信息会让报表给最终用户代码更多有效信息. 在报表中可以将地图作为图片添加进来,当一个图片显示在报表中时,该图片必须存放到本地计算机或者服 ...
- ONLYOFFICE连接数20个限制的由来
搜onlyoffice document server的github上的issue,会得到这2个地址https://github.com/ONLYOFFICE/DocumentServer/issue ...
- [经典Bug]Android-初始化闪屏不消失
问题描述: 业务上初始化过程要求显示闪屏界面,某个版本更新后,发现部分场景下,初始化完成后闪屏界面不消失. 问题原因: 初始化是在子线程进行,闪屏属于UI界面,需要UI线程展示.初始化过程和闪屏显示在 ...