在读这篇文章之前,也许你对单例模式的概念感到模糊或者不清楚,但是其实在日常的开发中你肯定用到过单例模式
那么接下来主要按照以下步骤进行讲述JS设计模式-------单例模式
1. 什么是单例模式
单例模式也称为单体模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点
举个栗子:一个班级只有一个班主任,只有一个太阳,一个国家只有一个主席这些 “唯一” “便于访问(全局访问)” 的行为对象便称作是单例
2. 如何实现单例模式
JavaScript`中的全局对象
全局对象是最简单的单例模式,利用ES6
的let
不允许重复声明的特性,刚好符合这两个特点
//举个栗子
let obj = {
name:"我是单例模式",
getName:function(){}
}
但是我们并不建议这么实现单例,因为全局对象/全局变量会有一些弊端:
注:单例不能够,乱用如果不满足 “唯一” 和 “全局访问”的 对象 千万不用成了单例
单例设计模式的实现:面向对象
// 单例设计模式的实现:面向对象
let Singleton = function(name) {
this.name = name;
this.instance = null;
}
Singleton.prototype.getName = function(){
return this.name;
}
Singleton.getInstance = function(name) {
if(!this.instance) {
this.instance = new Singleton(name);
}
return this.instance;
}
let instance1 = Singleton.getInstance('why');
let instance2 = Singleton.getInstance('www');
console.log(instance1===instance2); // 输出true
let obj1 = new Singleton('why');
let obj2 = new Singleton('www');
console.log(obj1.getName()); // 输出why
console.log(obj2.getName()); // 输出www
// 单例设计模式的实现:闭包
let Singleton = function(name) {
this.name = name;
}
Singleton.prototype.getName = function() {
return this.name;
}
Singleton.getInstance = (function() {
let instance = null;
return function(name) {
if(!instance) {
instance = new Singleton(name)
}
return instance;
}
})()
let instance1 = Singleton.getInstance('why');
let instance2 = Singleton.getInstance('www');
console.log(instance1 === instance2); // 输出true
无论以上面向对象的单例实现还是闭包的单例实现,都通过
Singleton.getInstance来获取Singleton类的唯一对象
,这增加了这个类的不透明性,使用者必须知道Singleton
是一个单例类,然后通过Singleton.getInstance
方法才能获取单例对象,要解决这一问题,可以使用透明的单例设计模式
// 透明的单例模式
let CreateDiv = (function(){
let instance = null;
let CreateDiv = function(html) {
if(instance) {
return instance;
}
this.html = html;
this.init();
instance = this;
return instance;
}
CreateDiv.prototype.init = function() {
let div = document.createElement('div');
div.innerHTML = this.html;
document.body.appendChild(div);
}
return CreateDiv;
})()
let instance1 = new CreateDiv('why');
let instance2 = new CreateDiv('www');
console.log(instance1===instance2); // 输出true
虽然上述透明的单例设计模式解决了不用通过
Singleton.getInstance
来获取单例类的唯一对象,但是在透明的单例设计模式中,构造函数CreateDiv
违反了单一职责,它不仅要负责创建对象,而且还要负责保证单例,假如某一天需求变化了,不再需要创建单例的div
,则需要改写CreateDiv
函数,解决这种问题,可以使用代理来实现单例模式
// 用代理实现单例模式
let CreateDiv = function(html) {
this.html = html;
this.init();
}
CreateDiv.prototype.init = function() {
let div = document.createElement('div');
div.innerHTML = this.html;
document.body.appendChild(div);
}
let ProxyCreateDiv = (function(){
let instance = null;
return function(html) {
// 惰性单例
if(!instance) {
instance = new CreateDiv(html);
}
return instance;
}
})()
let divInstance1 = new ProxyCreateDiv('why');
let divInstance2 = new ProxyCreateDiv('www');
console.log(divInstance1===divInstance2); // 输出true
惰性单例模式(懒汉式)
惰性单例是指的是页面开始加载的时候我们的实例是没有进行创建的,是当我们点击页面的div之后才开始创建实例(按需创建),这可以提高我们的网页性能,加快我们的页面渲染速度;
惰性单例又被成为懒汉式,相对应的概念是饿汉式:
饿汉式
class Window {
//直接进行创建
private static instance: Window = new Window();
public static getInstance() {
return Window.instance;
}
}
//把Window做成单例
let w1 = Window.getInstance();
let w2 = Window.getInstance();
console.log(w1 === w2);
惰性单例模式(懒汉式)
class Window {
// 存储单例
private static instance: Window;
public static getInstance() {
// 判断是否已经有单例了
if (!Window.instance) {
Window.instance = new Window();
}
//返回实例
return Window.instance;
}
}
//把Window做成单例
let w1 = Window.getInstance();
let w2 = Window.getInstance();
console.log(w1 === w2);//true
3. 何时会用到单例模式
页面多次调用都有弹窗提示,只是提示内容不一样
这个时候我们可以立马想到是单例模式,弹窗就是单例实例,提示内容是参数传递;我们可以用惰性单例模式来实现它;
let getSingleton = function (fn) {
let result;
return function () {
return result || (result = fn.apply(this, arguments)); // 确定this上下文并传递参数
}
}
let createAlertMessage = function (html) {
let div = document.createElement('div');
div.innerHTML = html;
div.style.display = 'none';
document.body.appendChild(div);
return div;
}
let createSingleAlertMessage = getSingleton(createAlertMessage);
document.getElementById('loginBtn').onclick = function () {
let alertMessage = createSingleAlertMessage('baidu');
alertMessage.style.display = 'block';
}
// 封装单例模式
var getSingle = function(fn) {
var result;
return function() {
return result || (result = fn.apply(this, arguments));
}
}
// 登录
var loginMsgbox = function() {
var dom = document.createElement('div');
dom.innerHTML = '登录弹框';
dom.style.display = 'none';
document.body.appendChild(dom);
return dom;
}
var createLoginMsgbox = getSingle(loginMsgbox);
document.getElementById('loginBtn').onclick = function() {
var login = createLoginMsgbox();
login.style.display = 'block';
}
当然还有用以下使用场景
4. 单例模式的优缺点
单例模式主要解决的问题就是节约资源,保持访问一致性。
简单分析一下它的优点:
单例模式也是有缺点的