浅析Javascript单例模式
定义
保证一个类仅有一个实例,并提供一个访问它的全局访问点 。就想我们在开发中有些对象只需要一个,例如window对象。
1. 实现单例模式
var Singleton = function( name ) {
this.name = name;
};
Singleton.prototype.getName = function() {
alert ( this.name );
};
Singleton.getInstance = function( name ) {
if ( !this.instance ) { //这里的this指的是Singleton,所以两次调用的都是同一个this
this.instance = new Singleton( name );
}
return this.instance;
};
var a = Singleton.getInstance( 'sven1' );
var b = Singleton.getInstance( 'sven2' );
alert ( a === b ); // true
我们通过 Singleton.getInstance 来获取 Singleton 类的唯一对象,这种方式相对简单,这种方式非常类似于Java的单例模式实现,但有 一个问题,就是增加了这个类的“不透明性”,Singleton 类的使用者必须知道这是一个单例类, 跟以往通过 new的方式来获取对象不同,这里偏要使用 Singleton.getInstance 来获取对象。 那么有没有办法直接New出来的就是一个单例的对象呢?
2.透明的单例模式
var Singleton = (function() {
var instance;
var Singleton = function(name) {
if ( instance ) {
return instance;
}
this.name = name;
return instance = this;
};
Singleton.prototype.getName = function() { //就近原则,用的是上面的Singleton
alert ( this.name );
};
return Singleton;
})();
var a = new Singleton( 'sven1' );
var b = new Singleton( 'sven2' );
console.log(a == b)
虽然现在完成了一个透明的单例类的编写,但它同样有一些缺点。
为了把 instance 封装起来,我们使用了自执行的匿名函数和闭包,并且让这个匿名函数返回,真正的 Singleton 构造方法,这增加了一些程序的复杂度,阅读起来也不是很舒服。 所以说有利有弊吧
3.Javascript中的单例模式
前面提到的几种单例模式的实现,更多的是接近传统面向对象语言中的实现,单例对象从 “类”中创建而来。在以类为中心的语言中,这是很自然的做法。比如在 Java 中,如果需要某个对象,就必须先定义一个类,对象总是从类中创建而来的。
但 JavaScript 其实是一门无类(class-free)语言(虽然ES6中有了类的概念),也正因为如此,生搬单例模式的概念并无 意义。在 JavaScript 中创建对象的方法非常简单,既然我们只需要一个“唯一”的对象,为什 么要为它先创建一个“类”呢?
单例模式的核心是确保只有一个实例,并提供全局访问。
问题:现在有一个需求是点击页面上的登录按钮,显示一个登录弹窗,很显然这个弹窗是唯一的,也就是单例的
(1)
<html>
<body>
<button id="loginBtn">登录</button>
</body>
<script>
var loginLayer = (function() {
var div = document.createElement( 'div' );
div.innerHTML = '我是登录浮窗';
div.style.display = 'none';
document.body.appendChild( div );
return div;
})();
document.getElementById( 'loginBtn' ).onclick = function(){
loginLayer.style.display = 'block'; };
</script>
</html>
上面解决方案是在页面加载完成的时候便创建好这个 div 浮窗,这个浮窗一开始肯定是隐藏状态的,当用户点击登录按钮的时候,它才开始显示,但是有时候我们根本不需要进行登录操作,如果一开始就被创建好其实是一种浪费资源的行为,DOM资源可是很珍贵的
(2)
<html>
<body>
<button id="loginBtn">登录</button>
</body>
<script>
var createLoginLayer = function() {
var div = document.createElement( 'div' );
div.innerHTML = '我是登录浮窗';
div.style.display = 'none';
document.body.appendChild( div );
return div;
};
document.getElementById( 'loginBtn' ).onclick = function(){
var loginLayer = createLoginLayer();
loginLayer.style.display = 'block';
};
</script>
</html>
上面的方法,虽然做到了点击时才创建,但失去了单例的效果。当我们每次点击登录按钮的时候,都会 创建一个新的登录浮窗 div。虽然我们可以在点击浮窗上的关闭按钮时(此处未实现)把这个浮 窗从页面中删除掉,但这样频繁地创建和删除节点明显是不合理的,也是不必要的。
(3)
<html>
<body>
<button id="loginBtn">登录</button>
</body>
<script>
var createLoginLayer = (function(){
var div;
return function() {
if ( !div ) {
div = document.createElement( 'div' );
div.innerHTML = '我是登录浮窗';
div.style.display = 'none';
document.body.appendChild( div );
}
return div;
}
})();
document.getElementById( 'loginBtn' ).onclick = function(){
var loginLayer = createLoginLayer();
loginLayer.style.display = 'block';
};
</script>
</html>
上述方法,用一个变量来判断是否已经创建过登录浮窗,和上面上面实现单例的类的原理是一样的
4.通用的单例模式
如果在项目中用到了很多单例的情况,如果每个都做判断,其实是相当复杂的事情,但能不能写一个通用的方法,经过这个方法处理的就是单例的
<html>
<body>
<button id="loginBtn">登录</button>
</body>
<script>
var getSingle = function(fn){
var result;
return function() {
return result || ( result = fn .apply(this, arguments ) );
}
};
var createLoginLayer = function() {
var div = document.createElement( 'div' );
div.innerHTML = '我是登录浮窗';
div.style.display = 'none';
document.body.appendChild( div );
return div;
};
var createMask = function() {
var div = document.createElement( 'div' );
div.innerHTML = 'mask';
div.style.display = 'none';
document.body.appendChild( div );
return div;
};
var createSingleLoginLayer = getSingle( createLoginLayer );
var createMask = getSingle( createMask );
document.getElementById( 'loginBtn' ).onclick = function(){
var loginLayer = createSingleLoginLayer();
var mask = createMask();
loginLayer.style.display = 'block';
mask.style.display = 'block';
};
</script>
</html>
我们发现在开发中并不会单独使用弹出窗,遮罩层和弹出窗是经常结合在一起使用,上面的方法就能使弹出窗和遮罩层都是单例的,其中核心的方法就是getSingle
在getSingle函数中,创建对象的方法 fn 被当成参数动态传入 getSingle 函数 ,result 变量因为身在闭包中,它永远不会被销毁。在将来的请求中,如果 result 已经被赋值,那么它将返回这个值 。
5. ES6中的单例模式
class SingletonApple {
constructor(name, creator, products) {
this.name = name;
this.creator = creator;
this.products = products;
}
//静态方法
static getInstance(name, creator, products) {
if(!this.instance) {
this.instance = new SingletonApple(name, creator, products);
}
return this.instance;
}
}
let appleCompany = SingletonApple.getInstance('苹果公司', '乔布斯', ['iPhone', 'iMac', 'iPad', 'iPod']);
ES6有类,也有静态方法,实现单例模式也显得非常的简单,学过Java的同学能看出几乎和Java的实现一模一样,直接调用SingletonApple的静态方法getInstance就能获得一个单例对象。
Javascript单例模式介绍到此结束,有不对的地方请及时指出
浅析Javascript单例模式的更多相关文章
- javascript单例模式的理解
javascript单例模式的理解 阅读目录 理解单例模式 使用代理实现单例模式 理解惰性单例 编写通用的惰性单例 单例模式使用场景 回到顶部 理解单例模式 单例模式的含义是: 保证一个类只有一个实例 ...
- 浅析 JavaScript 中的 函数 currying 柯里化
原文:浅析 JavaScript 中的 函数 currying 柯里化 何为Curry化/柯里化? curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字 ...
- 浅析 JavaScript 中的 函数 uncurrying 反柯里化
柯里化 柯里化又称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果. 因此柯里化的过程是 ...
- [转] JavaScript 单例模式
定义 确保一个类仅有一个实例,并提供一个访问它的全局访问点. 单例模式使用的场景 比如线程池.全局缓存等.我们所熟知的浏览器的window对象就是一个单例,在JavaScript开发中,对于这种只需要 ...
- Javascript单例模式概念与实例
前言 和其他编程语言一样,Javascript同样拥有着很多种设计模式,比如单例模式.代理模式.观察者模式等,熟练运用Javascript的设计模式可以使我们的代码逻辑更加清晰,并且更加易于维护和重构 ...
- 轻松掌握:JavaScript单例模式
单例模式 定义:保证一个对象(类)仅有一个实例,并提供一个访问它的全局访问点: 实现原理:利用闭包来保持对一个局部变量的引用,这个变量保存着首次创建的唯一的实例; 主要用于:全局缓存.登录浮窗等只需要 ...
- 浅析JavaScript访问对象属性和方法及区别
属性是一个变量,用来表示一个对象的特征,如颜色.大小.重量等:方法是一个函数,用来表示对象的操作,如奔跑.呼吸.跳跃等. 在JavaScript中通常使用”."运算符来存取对象的属性的值.或 ...
- 深入理解 JavaScript 单例模式 (Singleton Pattern)
概念 单例模式,也叫单子模式,是一种常用的软件设计模式.在应用这个模式时,单例对象的类必须保证只有一个实例存在. 核心:确保只有一个实例,并提供全局访问. 实现思路 一个类能返回对象一个引用(永远是同 ...
- 浅析JavaScript事件流——冒泡
一.什么是事件冒泡流 我们知道事件流指的是从页面中接受事件的顺序. 为了形象理解事件冒泡,可以想象三军主将诸葛亮,在帐内运筹帷幄,眼观六路耳听八方,这时候前方的战事情况就需要靠传令兵来传达,当第一位传 ...
随机推荐
- 根据dateFormatter创建NSDate类型数据
根据dateFormatter 2000-01-01 创建NSDate类型数据 NSDateFormatter *dateFormatter = [NSDate shareDateFormatter] ...
- mac搭建简单的hls推流服务器遇到的问题(待更新)
实际操作步骤: 输入brew install nginx-full --with-rtmp-module命令出现以下报错: 需要先安装nginx服务器,运行命令brew tap homebrew/ng ...
- sunTime
哈哈哈 开通了博客,有缘的你看能不能来到这里,或许我们认识呢
- vue 源码学习二 实例初始化和挂载过程
vue 入口 从vue的构建过程可以知道,web环境下,入口文件在 src/platforms/web/entry-runtime-with-compiler.js(以Runtime + Compil ...
- dijkstra算法解决单源最短路问题
简介 最近这段时间刚好做了最短路问题的算法报告,因此对dijkstra算法也有了更深的理解,下面和大家分享一下我的学习过程. 前言 呃呃呃,听起来也没那么难,其实,真的没那么难,只要弄清楚思路就很容易 ...
- 数据调试~~TCP转串口、串口转TCP调试
Android socket开发了一个socket客户端,当输入服务器ip以及端口,建立连接之后,Android可以发送数据到电脑接收服务器端. 如果电脑端没有socket服务器怎么办?方法如下: 1 ...
- DCOS实践分享(1):基于图形化模型设计的应用容器化实践
2015年11月29日,Mesos Meetup 第三期 - 北京技术沙龙成功举行.本次活动由数人科技CTO 肖德时 和 Linker Networks 的 Sam Chen 一起组织发起. 在这次m ...
- MongoDB 复制机制
一.复制原理 MongoDB的复制功能是使用操作日志oplog实现的,oplog包含主节点(Master)的每一次写操作,oplog是local本地数据库中的一个数据集合,其它非主节点(Seconda ...
- kali linux 网络渗透测试学习笔记(二)OWASP ZAP工具扫描SQL injection漏洞失败
按照惯例,利用OWASP ZAP工具扫描SQL injection漏洞时,应该很快就可以扫描出来,但是在笔者进行扫描的时候,却遇到了以下状况: 这说明了该工具根本就没能够扫描出SQL注入的漏洞,不知道 ...
- [Swift]LeetCode384. 打乱数组 | Shuffle an Array
Shuffle a set of numbers without duplicates. Example: // Init an array with set 1, 2, and 3. int[] n ...