参考自:https://www.kancloud.cn/cyyspring/more/1317334,还有多种模式的详细讲解,非常好的一个博主,此文章只做本人笔记用,建议移步就读博主文章
单例模式:只允许实例化一次的对象类。
利用闭包的保护机制防止代码冲突
使用场景
线程池
、全局缓存
、浏览器中的window对象
,即一个全局使用的类频繁地创建与销毁
举个例子:当点击页面的登录案例,
会弹出登录弹窗,这个弹窗无论点击多少次只能被创建一次,那么这个弹窗就可以
用单例模式创建
优点
1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例
2、避免对资源的多重占用
缺点
1、与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化
2、需要全局变量命名
普通单例模式
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
案例中的'a' 和'b' 打印的'name'都是'wang',也可以发现,实例只被创建了一次,也就是第一次
的'a',而'b'没有创建实例,还是用的'a'创建的实例。
上面单例模式写法的缺点
:这个单例实现获取对象的方式经常见于新手的写法,这种方式获取对象虽然简单,但是这种实现方式不透明
。知道的人可以通过 Singleton.getInstance() 获取对象,不知道的需要研究代码的实现,这样不好。这与我们常见的用 new 关键字来获取对象有出入,实际意义不大。
透明的单例模式
1、透明的单例模式是为了可以向正常创建实例一样通过'new'的形式使用
2、通过new来创建而不是像上个案例通过暴露出来的类方法,要做的就是需要将构造函数
暴露出来才能new成功,这里有个小知识点'构造函数中如果return 是一个对象,则返回的是
return 后面的对象
3、通过下面创建的'a' 和 'b',创建a的时候页面会生成一个div的dom节点并且里面是sven1,
但当创建'b'的时候不会有任何反应在页面上因为,a 和 b是同一个实例,且只有创建新的
实例的时候才会在构造函数内自动执行init,也变相说明'b'没有创建实例,当通过b调用
init方法的时候页面会生成一个div的dom节点并且里面是sven1,因为'b'不创建实例,因此
用的是'a'创建时候初始化赋的值
透明的单例模式
缺点
: 代码不易读不易改
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()
代理单例模式
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
惰性单例
1.惰性单例是指在需要的时候才创建,和之前的几个案例写法上是一样的,当
使用的时候去new 或者去调用静态方法创建实例
2.创建弹窗或者那些只要执行一次就可以全局使用的实例都可以用单例
- 例如
var Singleton = function (name) {
this.name = name
}
// 静态属性(类属性)
Singleton.instance = null
Singleton.prototype.getName = function () {
console.log(this.name)
}
// 静态
Singleton.getInstance = function (name) {
if(!this.instance){
this.instance = new Singleton(name)
}
return this.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
通用惰性单例
1、举个书中的例子,登陆的弹窗可能在ifram 中或者非ifram中都需要弹出,
那么我们使用单例模式的时候是否还需要在ifram中适配一个符合在ifram可以
通用的弹窗,这时候就可以使用'通用惰性单例'
2、再举个例子遮罩层,如果登录页的遮罩层和非登录页的不同,是否我们需要
写两个单例,一个给遮罩层用,一个给登录页用
通用代码
var singleton = function(fn) { var instance; return function() { return instance || (instance = fn.apply(this, arguments)); } };
- 弹窗案例代码
var singleton = function(fn) {
var instance;
return function() {
return instance || (instance = fn.apply(this, arguments));
}
};
// 创建遮罩层
var createMask = function(){
// 创建div元素
var mask = document.createElement('div');
// 设置样式
mask.style.position = 'fixed';
mask.style.top = '0';
mask.style.right = '0';
mask.style.bottom = '0';
mask.style.left = '0';
mask.style.opacity = 'o.75';
mask.style.backgroundColor = '#000';
mask.style.display = 'none';
mask.style.zIndex = '98';
document.body.appendChild(mask);
// 单击隐藏遮罩层
mask.onclick = function(){
this.style.display = 'none';
}
return mask;
};
// 创建登陆窗口
var createLogin = function() {
// 创建div元素
var login = document.createElement('div');
// 设置样式
login.style.position = 'fixed';
login.style.top = '50%';
login.style.left = '50%';
login.style.zIndex = '100';
login.style.display = 'none';
login.style.padding = '50px 80px';
login.style.backgroundColor = '#fff';
login.style.border = '1px solid #ccc';
login.style.borderRadius = '6px';
login.innerHTML = 'login it';
document.body.appendChild(login);
return login;
};
document.getElementById('btn').onclick = function() {
var oMask = singleton(createMask)();
oMask.style.display = 'block';
var oLogin = singleton(createLogin)();
oLogin.style.display = 'block';
var w = parseInt(oLogin.clientWidth);
var h = parseInt(oLogin.clientHeight);
}