单例模式
简单的单例模式
通过一个getInstance方法获取对象,首先判断一个对象是否存在,如果存在则就将该对象返回,如果不存在就先实例化对象(创建对象),然后再返回
将实例存储在对象中
let Singleton = function (name) {
this.name = name;
}
// 将实例存储在对象中
Singleton.getInstance = function (name) {
// 如果当前对象上不存在instance属性,那么就实例化一个对象,并且保存在属性instance上
if (this.instance === void (0)) {
this.instance = new Singleton(name);
}
// 返回该对象属性
return this.instance;
}
let aIns = Singleton.getInstance("test");
let bIns = Singleton.getInstance("ceshi");
console.log(aIns === bIns, aIns, bIns);
我们发现第一次将name参数传入后,后面获取再放入name参数并不会影响到原实例
使用闭包存储实例对象
// 将实例使用闭包的形式存储起来
Singleton.getInstanceNew = (function() {
// 存储单例对象
let instance = null;
// 返回了一个方法,这个方法引用了作用域外的变量,形成了闭包
return function(){
// 还是一样的操作
if(instance === null){
instance = new Singleton();
}
return instance;
}
}())
let cIns = Singleton.getInstanceNew();
let dIns = Singleton.getInstanceNew();
console.log(cIns === dIns, aIns === dIns);
我们看到以上两个方法,都是创建单例的简单方法,虽然实现了,但是同时有一定缺点
- 如果别人不知道这个对象时单例的,还是可以使用
new关键字创建。- 当使用
new创建后,实例不再是单例了,所以很容易违背了我们单例的初心
透明的单例模式
我们依然通过new创建实例,但是创建的还是单例的,这样即使不需要我们特意去嘱咐这个对象是单例
var TransparentSingletonBox = (function() {
// 存储单例对象
var instance = null;
// 构造函数引用了作用域外的变量,形成了闭包
var TransparentSingleton = function(){
if(instance){
return instance;
}
return instance = this;
}
// 返回了一个构造函数
return TransparentSingleton;
}())
let eIns = new TransparentSingletonBox();
let fIns = new TransparentSingletonBox();
console.log(eIns === fIns);
- 先执行的是自执行函数,并返回一个构造函数
TransparentSingleton- 这个构造函数内部引用了外部作用域【自执行的匿名函数作用域】的变量
instance,形成闭包- 外部
TransparentSingletonBox接受到内部返回的TransparentSingleton构造函数这种方法虽然满足了我们的要求,但是这里存在一个问题,就是内部的
TransparentSingleton的这对象,成为了一个私有对象,我们在外面无法访问到
代理版单例模式
var CreateDom = function(html){
this.html = html;
this.init();
}
CreateDom.prototype.init = function(){
console.log(this.html);
}
var ProxyCreateDom = (function(){
let instance = null;
return function(html){
if(instance === null){
instance = new CreateDom(html);
}
return instance;
}
}())
let hIns = new ProxyCreateDom("测试");
let gIns = new ProxyCreateDom("前端");
console.log(hIns === gIns, hIns.html, gIns.html)
这里我们将对象提了出来了,通过
ProxyCreateDom去创建一个关于CreateDom的单例,但是这个版本是比较有限的
- 我们可以看到这个代理单例并不是一个可复用的,代码很可能会翻倍,【一个对象对应一个代理单例】。
- 不管我们需不要单例,其实
ProxyCreateDom里的instance就创建了。- 这个是属于传统面向对象语言的,在JavaScript这个没有类的语言中,这样创建意义并不大【这个不是很理解,有大佬可以帮忙解释一下吗?】
惰性单例模式通用版本
let getSingleton = function(func) {
let result = null;
return function(){
return result || (result = func.apply(this, arguments));
}
}
function createLoginLayer(){
var div = document.createElement("div");
div.innerHTML = "我是登录浮窗";
div.style.display = "none";
document.body.appendChild(div);
return div;
}
/**
* 测试代码
*/
let createSingleLoginLayer = getSingleton(createLoginLayer);
// 添加一个登陆浮窗
document.getElementById("loginTest").onclick = function(){
var loginLayer = createSingleLoginLayer();
loginLayer.style.display = "block"
}
// 创建一个iframe的单例
var createSingleIfame = getSingleton(function () {
var iframe = document.createElement("iframe");
document.body.appendChild(iframe);
return iframe;
})
// 创建一个iframe,并将src属性设置为百度
document.getElementById("loginBtn").onclick = function(){
var loginLayer = createSingleIfame();
loginLayer.src = "http://baidu,com";
}
// 将浮窗单例隐藏
document.getElementById("closeLoginBtn").onclick = function(){
var loginLayer = createSingleLoginLayer();
loginLayer.style.display = "none"
}
// 改变iframe的src属性
document.getElementById("changeUrlBtn").onclick = function(){
var loginLayer = createSingleIfame();
loginLayer.src = "http://127.0.0.1:5500/index.html";
}