原文博客地址,欢迎讨论,star
偶然间看到有人使用ES6
的class
语法实现了一个比较好的单例模式,就想着结合所接触到的和网上一些讨论的实际例子来看看在javascript
中单例是怎么玩耍的,怎么应用的。
ES3/ES5 中的单例模式
在ES3/ES5
中,还没有class
这样的语法,之前最早接触设计模式的时候,一般网上的教程都是以java
来解释的,因为作为面向对象的语言确实好像能直观的去解释这些设计模式。
比如,单例模式,就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。
在javascript
中,是没有类这个东西的,全都是对象,所以在其中实现单例一般是使用一个子执行函数返回一个对象,这个对象去有个方法去获取instance
,
在获取的时候进行是否存在的判断。如下:
var Single = (function(){
var instance;
/*
* 这里面还可以定义一些私有的方法,主要是用到了闭包
*/
function get() {
/*
* 这里返回的就是最后被使用到的对象
*/
return {
doSomething: function () {
console.log("AAA")
}
}
}
return {
getInstance () {
return instance || (instance = get()) // 如果instance 变量是有值的就直接返回,如果是没有值的就调用生成对象返回并赋值给instance
}
}
})()
var instance1 = Single.getInstance()
var instance2 = Single.getInstance()
console.log(instance1 === instance2) // true
在ES3/ES5
一般都会使用如上组织形式去实现单例模式,接下来我们看一下在ES6
之后的场景。
ES6 之后使用 class 语法糖实现
ES6
引入class
关键字,一套很简洁用来实现js
里面构造函数的语法糖。如果对class
的用法还不是很熟悉的可以点击阮老师的es6入门
来学习一下。先看代码:
const single = 'single' // 这里使用symbol会好一点
class A {
static get instace () {
if (this[single]) { // 由于是静态函数,这里的this指的是A,并不是 new A() 产生的对象哦。
return this.single
}
return this[single] = new this() // 如果没有值就new 构造函数
}
constructor() {
const sourceClass = this.constructor // 获取构造函数对象
if (!sourceClass[single]) { // 判断对象上面是否已经有了单例
sourceClass[single] = this // 这里的this指的是已经构造好的对象,空对象,只是constructor指向A
}
return sourceClass[single] // 如果已经存在则直接返回
}
}
上面这份代码还是需要点较深ES6
的知识才能看明白的,这个”类“里面有两个方法来产生实例。
- 第一种就是构造函数了,会判断返回一个实例
- 第二个就是有一个静态属性也会经过判断返回一个实例。
里面使用了
this
来指向A
这个构造函数(构造函数也是对象),我代码上也做了注释。关键点就是,根据指定key
来挂载这个实例,但是封装性更好了一点。
利用 nodejs 模块化实现单例子
其实这里说实现不是很准确,只能说目前nodejs
和ES6
的模块化中的每个模块其实就是单例的。下面我们来先看一下代码:
class A {
doSomething() { ... }
}
modules.export = new A() // 这里直接将对象生成,然后导出
因为nodejs
和ES6
中每个模块的代码只会被加载一遍,第二次或者第三次等等,就会从cache
里面去查找,是否已经加载过,就直接使用了。
- 首先我们创建一个
a.js
,在里面随便打印一句话。 - 打开
node
,导入这个文件。 - 我们查看一下
module
可以看到这个模块已经被存储下来了,之后回去就会根据id(模块的路径)判断,是否需要执行该模块的代码。
一些应用场景
- 网页上的
loading
,整个网页可以只创建一个实例,根据传入不同的参数去渲染不同的样式或者行为,全局通用。 nodejs
里面的数据库链接实例,访问一次创建一次链接是很消耗资源的,所以也是全局的查询操作都是统一使用一个实例。
总结
单例模式还是非常简单,也很实用。目前还可以实用ES6
的代理去修改对象的创建来达到单例。这些只是实现方法,我们需要多多思考,那些场景这些设计模式来达到代码的可扩展性和可复用性等。