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代码, ...
随机推荐
- 对datagridview进行增删改(B)
create DATABASE stu ON ( name='stu.mdf', filename='F:\胡浴东\数据库\stu数据库\stu.mdf', size=, filegrowth= ) ...
- vb.net 使用MD5密碼加密
Function MD5(ByVal strSource As String, ByVal Code As Int16) As String'使用MD5加密 Dim dataToHash As Byt ...
- 无法外网访问VM中的hadoop yarn的8088端口
1.检查是否正确的启动了resourcemanager服务 若是没有启动,请检查yarn-site-xml配置 2.若是启动了 1.检查客户机和虚拟机之间是否能够相互ping通 2.检查虚拟机防火墙是 ...
- js-ES6学习笔记-修饰器
1.修饰器对类的行为的改变,是代码编译时发生的,而不是在运行时.这意味着,修饰器能在编译阶段运行代码. 2. function testable(target) { target.isTestable ...
- Intellij Idea出现 unable to establish loopback connection
项目一运行就出现这个情况,好几次了,最后发现只要防火墙关闭,项目就可以运行成功.错误提示:“C:\Program Files\Java\jdk1.8.020\bin\java” -Xmx700m -D ...
- centos7下采用Nginx+uwsgi来部署django
之前写过采用Apache和mod_wsgi部署django,因为项目需要,并且想比较一下Nginx和Apache的性能,尝试采用Nginx+uwsgi的模式来部署django. 1.安装uwsgi以及 ...
- Origin绘制双Y轴图的方法
1.已有数据绘图如下,其中网络流量的单位是M Bytes/s,与另外两组数据的单位(时间)不同,现在要为其添加右侧Y轴. 2.首先选中该图像,找到工具条,点击第三个按钮“Add Right-Y Lay ...
- vue-router 手势滑动触发返回
vue-router的路由变换只存在“变换前”和“变换后”,不存在“切换中”的状态,所以做不到大多数app(微信那样的)在滑动过程中让界面跟随手指移动.但滑动事件还是可以监听的,我们可以在滑动之后再触 ...
- Python+Selenium笔记(十四)鼠标与键盘事件
(一) 前言 Webdriver高级应用的API,允许我们模拟简单到复杂的键盘和鼠标事件,如拖拽操作.快捷键组合.长按以及鼠标右键操作,都是通过使用webdriver的Python API 中的Ac ...
- python request 接口自动化设计
设计思路: * 数据驱动 * 测试结果以邮件形式发送 * 保留测试过程的用例和请求结果到日志,方便查问题 设计如下: * bin * casehandler 读取txt或者xls文件中的用例,一个文件 ...