单例模式,从字面意思来看,比较容易理解,单个实例的模式,这也是单例模式的主要思想,思想大概主题时限制一个类只能有一个实例化对象,大概意思就是,通过判断来分析是否存在实例化对象,存在则返回已实例化的对象,如果不存在则实例化且返回对象,但是也并不是所有的单例模式思想都要从构造函数观点出发,不是实例也可以体现出单例模式的优点
基础单例模式
- 这里说的基础的单例模式实际上与网上大部分文章讲的不一样 其实不从类出发,更容易理解,这里说一下单例模式的主要优点,第一个优点就是,解决全局变量的污染,我们在开发的过程中,经常会定义变量,然后变量越来越多,我们起名字越来越麻烦越来越复杂,为了保证语义化又保证变量的命名简单,很容易一不小心就命名了相同的变量,当然了我们现在都有检查命名是否重复等能力,但是在拆分文件又要定义全局的变量的时候,我们又会有相同的问题,检查不出来,而且会令人头疼
var Dom=(function(){//ajax模块var ajax ={get:function(){},
post:function(){}}//dom模块var dom ={get:function(){},
create:function(){}}return{
ajax: ajax,
dom: dom,}})()
我们看这段代码,我们实际上在原本的开发习惯上,会创建两个对象,一个是ajax,一个dom,这两个对象各有自己的属性或者说方法,那么在原有逻辑,肯定是要创建全局变量的,这样才能够令全局访问到,但是回过头来看,我们创建的dom,难道只保证有一个吗,如果有一个作用相同的对象,我们怎么命名,分开命名为dom1,dom2? 还是说语义化的加上dom-xxx,总之是复杂的,全局空间就这样被污染了
这个只是一个简单的例子,实际上的单例模式并不是这样的,我主要是借助这个来说明一下单例模式的思想以及出发点
单例模式
- 真正的单例模式,核心思想就是,
保证一个类仅有一个实例,并提供一个访问它的全局访问点
- 真正的例子:
为什么我们在任何文件引入的vue和vuex都是同一个,这就是单例模式的使用
let SingleCase=(function(){let instance;returnfunction(name){if(instance){return instance;}return instance = name;}})();const singl1 =SingleCase('我是第一次调用')const singl2 =SingleCase('我是第二次调用')
console.log(singl1==singl2)// true
- 浓缩的都是精华,这里看下,单例模式用了什么东西
闭包
,复习一下闭包都有什么能力,缺点我们都知道会导致内存泄漏,因为内部使用外部的变量会导致外部变量永远无法被释放,这样就导致了有一块内存被永久占用也就是内存泄漏,那双刃剑,用好了也是有好处的,我们的全局变量有啥缺点,也是不会被释放,那么我们直接使用闭包来做一些事情实际上我们本身没有什么损失 - 我们用了一个instance变量,这个变量在内部return的函数中被使用,也是一直不会被释放的,那么为什么第一次跟第二次调用函数出来的结果完全一样,因为我们在内部做了
判断
,有人不是说了,所有代码的基础都是ifelse,判断instance是不是已经存在的第一次
的时候是不存在的,那么我们就会把传值name赋值给instance第二次
调用的时候我们经过判断,我们已经赋值过一次了,那我们就直接返回已经赋值过一次的变量instance 所以两次打印一定都是我是第一次调用
透明单例模式
- 这个实际上才是真实的单例模式,在用的时候可以跟实例化所有的构造函数一样的用法,new就够了
var Single=(function(){var instance;returnSingleCase=function(){if( instance ){return instance;}return instance =this;};})();var Single1 =newSingle();var Single2 =newSingle();
与上边相比改动代码不多,只是把返回函数的返回值改为了this,关于this指向问题可以网上查一下,我只在这里简单解释一下
- 实例化构造函数的时候,构造函数内的this指向实例,第一次调用的时候把第一个实例的this指向通过instance变量闭包保存起来,第二次实例化对象的时候直接返回了第一次保存的this,那第二次实例化就直接跟第一次实例化的this指向相同,两个相同的this指向,肯定会指向同一个实例,也就是第一次实例化的实例
- 在这个例子中我们看到了怎么去保证一个类只有一个实例,且是完全透明化的,在开发的过程中完全不用另外的写法,正常实例化对象即可
与代理模式结合
- 代理模式是昨天梳理过的模式,有几个用法,一个就是验证代理,一个就是缓存代理,这两个代理其实与单例模式完全可以结合起来
- 验证代理能做到什么,做到在实例化之前拦截,判断是要使用单例模式还是不用
- 缓存代理能做到什么,做到在实例化的时候根据参数判断我们是否实例化过相同的实例
当然了,并不是说我们的代理模式是可以与单例模式达到什么惊人的效果,只是说代理模式能和单例模式共同使用
//验证代理去使用单例//单例不变,代理拦截逻辑如下functionProxy(password){if(password==='实例化单例密码'){returnnewSingle()}else{return'没密码还来实例化,不允许'}}var Single1=Proxy('实例化单例密码')var Single2=Proxy('我没密码')
这是一个简单的示例,只是一个思路,并不是说代理模式与单例模式结合就是这样的形式,只是表达一下中心思想,
可以联合使用
惰性单例
最后,借用一个惰性单例的例子,是网上一个实用型的按钮点击触发弹窗的例子
let createLoginPop =(function(){let loginDiv;// 闭包 : 该对象常存returnfunction(){if(!loginDiv){
loginDiv = document.createElement('div');
loginDiv.style.display ='none';
document.body.appendChild(loginDiv);
loginDiv.onclick=function(){
loginDiv.style.display ='none';};}return loginDiv;
}})()// 在使用的时候才创建登陆div
document.getElementById('btnLogin').onclick=function(){let loginDiv =createLoginPop();
loginDiv.style.display ='block';}
- 很典型也很简单,就是看弹窗是否已经创建且插入过了,如果没有就进行一系列操作,如果有的话就直接返回之前插入过的那个dom对象,直接对已有的dom对象进行显示与隐藏操作,当然了,现在用框架这种逻辑肯定是用不到的,有多种方式可以避免反复创建dom元素和插入,但是这个思维是实用在具体逻辑上最为简单和基础的,可以仔细看看
所有的设计模式我们梳理或者说学习的过程中,都一定要从浅处入手,从概念入手,有所运用与实践固然是好的,但是一个概念的学习更加重要,照本宣科是无法理解到精髓的,按照某种设计模式的例子来做事情,肯定不行,肯定是要体会中心思想的,想要深入的可以建议看一下
vuex的源码
,相对来说少一些也好解读一些,读源码比较困难的可以看vuex的源码解析,
针对性的阅读源码,是比通读进步更加快的方式
,单例模式思想就在其中有所体现
版权归原作者 Absorbed_zhang 所有, 如有侵权,请联系我们删除。