Skip to content

Latest commit

 

History

History
166 lines (131 loc) · 5.29 KB

设计模式——单例模式.md

File metadata and controls

166 lines (131 loc) · 5.29 KB
title date tags summary
设计模式——单例模式
2018-10-14
DesignMode
Javascript
单例是一种常用的模式,有一些对象我们往往只需要一个,比如线程池、全局缓存、浏览器中的window对象等。

开场白

单例模式的定义是:保证一个类仅有一个实例,并提供一个访问它的全局访问点

单例是一种常用的模式,有一些对象我们往往只需要一个,比如线程池全局缓存浏览器中的window对象等。

用js实现单例

// 实现并不复杂,核心是利用一个变量来标志当前是否已经为某个类创建了单例
var Singleton = function(name){
    this.name = name;
    this.instance = null; // 标记和存放单例实例的容器
}
Singleton.prototype.getName = function(){
    return this.name;
}
Singleton.getInstance = function(name){
    if(!this.instance){ // 如果没有实例就创建并返回它
        return this.instance = new Singleton(name);
    }
    // 如果已经有实例就返回它
    return instance;
}
// 创建单例
var obj1 = Singleton.getInstance('william');
var obj2 = Singleton.getInstance('skye');

console.log(obj1===obj2); // true;

缺点即是不能通过new XXX方式获取实例,只能通过类提供的getInstance方法获取单例实例,容易被人在不知情的情况下直接new Singleton出没有单例的实例来。

实现透明的单例模式

可以实现通过new XXX方式获取实例,这样即可完成一个透明的单例

// 将构造函数封存在闭包
var Singleton = (function(){
    var instance; // 标记和存放单例实例的容器
    
    var Singleton = function(name){
        if(instance){
            return instance;
        }
        this.name = name;
        // this.init(); // 一些初始化的操作,如有必要
        return instance = this;
    }
    Singleton.prototype.init = function(args){
        // todo;
    }
    return Singleton;
})();
// 创建单例
var obj1 = new Singleton('william');
var obj2 = new Singleton('skye');

console.log(obj1 === obj2); // true

为了把instance封装起来,我们使用了自执行的匿名函数和闭包,并且让这个函数将真正的Singleton构造方法返回出来,这增加了一些复杂度,阅读起来也不舒服。

利用代理实现单例模式

这种在上述的代码中做了些调整,将构造单例的工作委托给了一个代理类,这样在开发中可以更加灵活的使用单例或者非单例;

// 依然创建一个普通的类
var Singleton = function(name){
    this.name = name;
}

// 接下来引入代理类,将创建单例的工作委托给代理类
var ProxySingleton = (function(){
    var instance; // 标记和容器
    
    return function(name){
        if(!instance){
            return instance = new Singleton(name);
        }
        return instance;
    }
})();

// 创建单例
var obj1 = new ProxySingleton('william');
var obj2 = new ProxySingleton('skye');

console.log(obj1 === obj2); // true

惰性单例

在需要的时候才创建对象,并且只创建唯一的一个。

在最开始的单例实现上就用了这种思想,当我们调用Singleton.getInstance的时候才创建单例,而不是在页面加载好的时候就创建。

如果把通用的一些逻辑抽象出来就可以实现一个通用的惰性单例

单例的核心就是通过一个变量来标志是否创建了单例

// 核心逻辑
var obj;
if(!obj){
    obj = xxx;
}

// 将这个逻辑抽离出来,封装在getSingleton函数内部,并将创建对象的方法fn当作参数动态地传入getSingleton
var getSingle = function(fn){
    var instance;
    return function(){
        return instance || (instance = fn.apply(this.arguments));
    }
}

// 使用
var createSingleLoginLayer = function(){
    //创建一个登陆弹窗
    var div = document.createElement('div');
    div.innerHTML = '我是登陆浮窗';
    div.style.display = 'none';
    document.body.appendChild(div);
    return div;
}
var createSigleLoginLayer = getSingle(createLoginLayer);
// 绑定按钮,点击创建唯一的浮窗
document.getElementById('loginBtn').onclick = function(){
    var loginLayer = createSigleLoginLayer();
    loginLayer.style.display = 'block';
}

// 创建一个登陆iframe
var createSingleIframe = getSingle(function(){
    var iframe = document.createElement('iframe');
    document.body.appendChild(iframe);
    return iframe;
})
// 绑定按钮,点击创建唯一的iframe
document.getElementById('loginBtn').onclick = ()=>{
    var loginLayer = createSingleIframe();
    loginLayer.src = 'http://baidu.com';
}

小结

​ 首先学习了传统的单例模式的实现,也了解到因为语言的差异性,有更适合的方法在js中创建单例。特别是在合适的时机才创建唯一对象的惰性单例技术。更奇妙的是,创建对象和管理单例的职责被分布在两个不同的方法中,这两个方法组合起来才具有单例模式的威力。

关注本人公众号