[TOC] >[success] # 什么是单例模式 * [推荐参考看的文章](https://segmentfault.com/a/1190000012842251#articleHeader5) ~~~ 1.保证一个类仅有一个实例,并提供一个访问它的全局访问点 '意图:' 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 '主要解决:'一个全局使用的类频繁地创建与销毁。 '何时使用:'当您想控制实例数目,节省系统资源的时候。 '如何解决:'判断系统是否已经有这个单例,如果有则返回,如果没有则创建。 2.因此要实现一个单例模式无非是使用一个变量来标记当前是否已经为某个类创建 了对象,如果创建则在下一次直接获取之前返回创建的实例 ~~~ >[danger] ##### 使用场景 ~~~ 1.线程池、全局缓存、浏览器中的window对象 举个例子:当点击页面的登录案例, 会弹出登录弹窗,这个弹窗无论点击多少次只能被创建一次,那么这个弹窗就可以 用单例模式创建 ~~~ >[danger] ##### 优缺点 * 优点 ~~~ 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例 2、避免对资源的多重占用 ~~~ * 缺点 * [单一职责](https://blog.csdn.net/zhengzhb/article/details/7278174/) ~~~ 1.与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化 ~~~ >[danger] ##### es5 实现单例模式 ~~~ 1.下面的案例根据上面的分析想单例模式,首先需要一个可以用来标记是否已经创建实例的属性, 下面案例用的是'instance '做的这件事,因此特意用的是类属性来标记,并且对外提供可一个方法 用来'getInstance '也是静态方法,用来做实例是否是第一次创建 2.通过下面案例的'a' 和'b' 打印的'name'都是'wang',也可以发现,实例只被创建了一次,也就是第一次 的'a',而'b'没有在创建了实例还是用的'a'创建的实例 3.下面单例模式写法的缺点:这个单例实现获取对象的方式经常见于新手的写法,这种方式获取对象 虽然简单,但是这种实现方式不透明。知道的人可以通过 Singleton.getInstance() 获取对象, 不知道的需要研究代码的实现,这样不好。这与我们常见的用 new 关键字来获取对象有出入, 实际意义不大。 ~~~ ~~~ var Singleton = function (name) { this.name = name } // 静态属性(类属性) Singleton.instance = null Singleton.prototype.getName = function () { console.log(this.name) } // 静态 Singleton.getInstance = function (name) { if(!Singleton.instance){ Singleton.instance = new Singleton(name) } return Singleton.instance } var a = Singleton.getInstance('wang') var b = Singleton.getInstance('Yi') console.log(a.name) // wang console.log(b.name) // wang console.log(a === b ) // true ~~~ * 第二种用getInstance 方法用闭包的方式 ~~~ var Singleton = function (name) { this.name = name } Singleton.prototype.getName = function () { console.log(this.name) } // 静态 Singleton.getInstance =( function () { var instance = null return function (name) { if(!instance){ instance = new Singleton(name) } return instance } })() var a = Singleton.getInstance('wang') var b = Singleton.getInstance('Yi') console.log(a.name) // wang console.log(b.name) // wang console.log(a === b ) // true ~~~ >[danger] ##### 透明的单例模式 ~~~ 1.透明的单例模式是为了可以向正常创建实例一样通过'new'的形式使用 2.下面案例首先整体思路和单例模式还是一样,需要一个值用来记录创建 的实例,如果已经创建则用记录的实例,如果没有创建则创建一个新的实例 3.但为了实现,现在的构想,想通过new来创建而不是像上个案例通过暴露 出来的类方法,要做的就是需要将构造函数暴露出来才能new成功,这里有个 小知识点'构造函数中如果return 是一个对象,则返回的是return 后面的对象' 4.下面的案例看似复杂,实际就做了一件事,当对象第一次创建的时候调用他的 初始化方法,初始化方法是用来生成div标签的,并且返回当前实例且记录下来当 再一次使用的时候如果有则返回记录的实例 5.通过下面创建的'a' 和 'b',创建a的时候页面会生成一个div的dom节点并且里面是sven1, 但当创建'b'的时候不会有任何反应在页面上因为,a 和 b是同一个实例,且只有创建新的 实例的时候才会在构造函数内自动执行init,也变相说明'b'没有创建实例,当通过b调用 init方法的时候页面会生成一个div的dom节点并且里面是sven1,因为'b'不创建实例,因此 用的是'a'创建时候初始化赋的值 6.缺点代码不易读不易改 ~~~ ~~~ var CreateDiv = (function (html) { var instance // 实际创建的构造函数,也就是最后实际 // 生成的实例的构造函数 var CreateDiv = function (html) { if(instance){ return instance } this.html = html this.init() return instance = this // 返回第一次创建的实例并且通过instance记录 }; // 初始化创建方法,会创建div标签 CreateDiv.prototype.init = function () { var div = document.createElement('div') div.innerHTML = this.html document.body.appendChild(div) } return CreateDiv })() var a = new CreateDiv('sven1') var b = new CreateDiv('sven2') b.init() ~~~ >[danger] ##### 使用代理模式创建 ~~~ 1.代理模式:自己不去做,委托中间人做 2.Person是一个普通类,通过new Person可以创建一个对象 3.用代理模式创建CreateSinglePerson方法,通过new CreateSinglePerson可以创建一个单例 4.这个写法相当于刚才两种已经不错了,既可以读懂,又是通过new创建的 ~~~ ~~~ function Person(name) { this.name = name; } Person.prototype.getName = function () { console.log(this.name); }; var CreateSinglePerson = (function () { var instance; return function (name) { if (!instance) { instance = new Person(name); } return instance; }; })(); var a = new CreateSinglePerson('a'); var b = new CreateSinglePerson('b'); console.log(a === b); // true var c = new Person('c'); var d = new Person('d'); console.log(c === d); // false ~~~