不是前言的前言
说实话,写文章好麻烦啊,看书,查资料,反复确认,自己学会了,理解了原理,还要用语言,文字,例子去描述出来,保证大部分人看懂。
但是!有人说但是之前的话毫无意义,我想还是有意义的。因为在写文章的过程中,我又升华了,又深刻了,又接受不完美的我了。
前言
这次我将开始围绕javascript设计模式开始学习、写作。
本文是我学习过程的总结归纳,与输出学习结果的尝试,尽量写的细致一些,直白些,硬性指标和软性解释都尽量写出来,保证新手小白们都能看懂,学会(我能学明白,大部分人应该都能学明白~love&peace)。
阅读本文之前请达成以下先决条件:
- 熟悉,掌握JavaScript关于类的知识点
- 熟悉,掌握,在实操代码的帮助下能够理解闭包
- 了解什么是模式,推荐阅读本人之前文章《什么是模式》,对模式有个初步印象。
单例(Singleton)模式
单例模式又称单体模式,它的本质作用是:限制类只能实例化一次。
实现的过程概括为一句话就是:如果已存在实例,则返回其引用,如果不存在,则创建当前类的实例并返回(如果你已有女票,则直接嘿嘿嘿,如果你没有,则找个女票,然后嘿嘿嘿)。
优点:在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如弹窗),避免对资源的多重占用。
缺点:没有接口,不能继承。
字面量对象单例
首先啰嗦一下,我本来不打算写字面量对象单例的,感觉这个没啥用,因为太常见了,我刚见这个情形时我都在怀疑这是单例吗?
但后来发现不得不写,因为不写这段,我感觉后面的有关惰性单例(延迟执行)没办法解释清楚。
实现代码:
var singleton = {
attr : 1,
method : function(){ return this.attr; }
}
var t1 = singleton ;
var t2 = singleton ;
console.log(t1===t2);//true
console.log(t2.method());//1
至此,因为 t1 和t2都引用的是同一个singleton对象,内存中指针都是指的一个地址,所以相等。
而对于方法 method,singleton充当共享资源命名空间,从全局命名空间中隔离出代码实现,从而为函数提供单一访问点。
所以singleton 就是平时很常见的字面量对象,你可以把 singleton 看做已初始化的实例对象,或者把singleton 换一种形式可能更容易帮助你理解:
var singleton = new Object({
attr: 1,
method: function () { return this.attr; }
});
var t1 = singleton;
var t2 = singleton;
console.log(t1 === t2);//true
console.log(t2.method());//1
你会发现,singleton 其实就是Object构造函数的实例化对象,只不过字面对象简化了操作而已,而且它符合单例的本质:限制类只能实例化一次。它确实只实例化了一次。
如果你再new一个具有相同的属性和方法的实例对象,那么是不是再次实例化了呢?答案是:false;
var singleton = new Object({
attr: 1,
method: function () { return this.attr; }
});
var other = new Object({
attr: 1,
method: function () { return this.attr; }
});
var t1 = singleton;
var t2 = other;
console.log(t1 === t2);//false
为什么?
虽然是具有相同的属性和方法的实例对象,但是本质上,是不相同的,因为分配进内存中的地址不相同,单例模式真正的实质每次调用构造函数时,返回指向同一个对象的指针,显然上述的的方式不符合该要求。(关于内存中地址的分配问题,如果你没相关的知识背景或者没有了解过,不用过分纠结。暂时可理解为:同名同姓的两个人【具有相同的属性和方法的实例对象】,但现实生活中并不是一个人【内存中的地址不相同】。)
通过闭包构建的防篡改单例
示列中的代码我是直接复制《JavaScript设计模式》的代码,在最后的参考资料中我会附上地址。
实现代码:
var mySingleton = (function () {
// 实例保持了Singleton的一个引用
var instance;
function init() {
// Singleton
// 私有方法和变量
function privateMethod() {
console.log("I am private");
}
var privateVariable = "Im also private";
var privateRandomNumber = Math.random();
return {
// 公有方法和变量--真正可以被实例访问使用的方法和变量
publicMethod: function () {
console.log("The public can see me!");
},
publicProperty: "I am also public",
getRandomNumber: function () {//公有方法返回了私有变量--对外的接口
return privateRandomNumber;
}
};
};
//立即执行函数返回 getInstance接口,通过闭包保持着 instance的引用。
return {
// 获取Singleton的实例,如果存在就返回,不存在就创建新实例
getInstance: function () {
if (!instance) {
instance = init();
}
return instance;
}
};
})();
var t1 = mySingleton.getInstance();
var t2 = mySingleton.getInstance();
console.log(t1 === t2);//true
单例模式的标准实现代码如上,其中有以下几个知识点:
- 立即执行函数返回init类对外唯一实例化接口getInstance,没有其他方式获取实例。
- 通过闭包,保持着instance对inti类实例的唯一引用,且不可修改。
- getInstance函数达成了单例模式要求(限制类只能实例化一次,如果已存在实例,则返回其引用,如果不存在,则创建当前类的实例并返回)
- 延迟创建(执行)
为何延迟执行对于Singleton很重要?
这句话是是原书中的原句问题,看了书中的的解释很模糊(因为我没有C++编程经验,也没能理解它并不能伸缩是什么意思。你看,我并不聪明。)原文如下:
在C++中,Singleton负责隔绝动态初始化顺序的不可预知性,将控制权归还给程序员。
值得注意的是类的静态实例(对象)和Singleton之间的区别:当Singleton可以作为一个静态的实例实现时,它也可以延迟构建,直到需要使用静态实例时,无需使用资源或内存。如果我们有一个可以直接被初始化的静态对象,需要确保执行代码的顺序总是相同的(例如:在初始化期间objCar需要objWheel的情况),当我们有大量的源文件时,它并不能伸缩。
但是,我在本文开始时举了关于字面量对象单例的栗纸~可以帮助理解一下为何延迟执行对于Singleton很重要。
字面量对象-书写即创建执行。JS执行逻辑是逐行执行,遇到字面量对象,即刻创建,即刻分配内存地址。而单例延迟执行,则是按需调用(很像函数不是吗?),避免了不必要的浪费。
很诚恳的说,其实是上述强行解释了一波,实际上文所指的内容,如静态类,其实JS中并不存在。但是,模拟一下还是可以滴:
var StaticClass = function(){};
StaticClass.staicVariable = "staicVariable";
StaticClass.staicMethod = function(){
console.log('模拟一下');
};
console.log(StaticClass.name); //'staicVariable'
StaticClass.staicMethod(1,3); //'模拟一下'
如果你有了解,学习过是PHP的话(我只学过PHP,所以用PHP举例),那么下面这段话可以帮助你更好的理解:
静态方法在程序开始时生成内存,实例方法在程序运行中生成内存,
所以静态方法可以直接调用,实例方法要先成生实例,通过实例调用方法,静态速度很快,但是多了会占内存。
单例模式的应用场景
-
全局唯一模态框/对话框/弹窗
-
全局缓存等
总结
首先说的还是关于内存分配的问题,如果不了解,或者感觉很吃力的话,可以暂时先放下内存问题不必钻牛角尖,当学习到一定程度时,自然会迎刃而解的。
其次是单例模式只是比较简单的设计模式,而我写了很多废话,扣字眼的去逐一解释,不是为了显示我是如何的博学与聪明,而是我学习过程中实实在在遇到的问题与疑惑,其实网上好多文章就是贴个代码,写个注释,知道怎么写单例就行了,而我学习时,学会写很快,一分钟吧,复制一下,示例代码,输出一下结果,看下结构就明白了,但感觉总是很模糊不清,不知其所以然,希望这篇文章能对同样在学习的小伙伴们有所帮助。
最后,共同学习进步,别放弃,想想你爱的人和爱你的人。
参考资料:
《JavaScript设计模式》——9.4 Singleton(单例)模式
依然再有人吐槽徐涛的翻译...下面是英文原版
《JavaScript设计模式》 [美]Addy Osmani 开源英文原版