单例模式深入理解与实践:以Storage类为例的详细解析
设计模式概述与单例模式的重要性
设计模式是软件工程领域的一个重要概念,它们提供了一系列经过验证的解决方案,帮助开发者解决常见的软件设计问题。单例模式,作为23种GoF设计模式之一,其核心思想是确保某个类只有一个实例,并提供一个全局访问点。这种模式在需要控制资源消耗、优化性能,或者简化对象间通信的情况下尤其有用。
单例模式在前端开发中的应用场景
在前端开发中,单例模式常被用于以下几种场景:
- 资源管理:例如,使用单例模式封装
LocalStorage
或SessionStorage
,可以确保对存储空间的统一管理和高效利用。 - 全局状态管理:在复杂的应用中,单例模式可以帮助管理全局状态,避免多个组件或服务之间产生冲突。
- 服务和工具类:对于那些不需要多次实例化的服务和工具类,如日志记录、网络请求等,单例模式可以提高性能,减少内存消耗。
在ES6中,利用class
和static
关键字可以简洁地实现单例模式:
class Storage {
constructor() {
if (Storage.instance) {
throw new Error('Use getInstance() to get the single instance of this class.');
}
Storage.instance = this;
}
static getInstance() {
if (!Storage.instance) {
new Storage();
}
return Storage.instance;
}
getItem(key) {
return localStorage.getItem(key);
}
setItem(key, value) {
localStorage.setItem(key, value);
}
}
这里的constructor
检查是否已有实例,如果没有,则创建并保存;如果有,则抛出错误阻止重复创建。getInstance
方法则负责返回这个唯一实例。这样,无论何时调用Storage.getInstance()
,都会得到同一个实例,保证了单例性。
4. ES5及之前的单例模式实现
在不支持ES6的环境中,我们可以通过闭包来实现单例模式:
const Storage = (function() {
let instance;
function Storage() {
if (instance) {
throw new Error('Use getInstance() to get the single instance of this class.');
}
instance = this;
}
Storage.prototype.getItem = function(key) {
return window.localStorage.getItem(key);
};
Storage.prototype.setItem = function(key, value) {
window.localStorage.setItem(key, value);
};
Storage.getInstance = function() {
if (!instance) {
new Storage();
}
return instance;
};
return Storage;
})();
const storageInstance = Storage.getInstance();
这里,instance
变量仅在此作用域内可见,从而保证了单例的特性。getInstance
方法同样用于获取或创建实例。
增强功能:添加缓存机制
在单例模式的基础上,我们可以进一步增强Storage
类的功能,比如添加一个简单的缓存机制,这有助于减少对本地存储的频繁读取,从而提升性能。
class Storage {
constructor() {
if (Storage.instance) {
throw new Error('Use getInstance() to get the single instance of this class.');
}
Storage.instance = this;
this.cache = {};
}
static getInstance() {
if (!Storage.instance) {
new Storage();
}
return Storage.instance;
}
getItem(key) {
if (this.cache[key]) {
return this.cache[key];
}
const value = localStorage.getItem(key);
this.cache[key] = value;
return value;
}
setItem(key, value) {
localStorage.setItem(key, value);
this.cache[key] = value;
}
clearCache() {
this.cache = {};
}
}
这里,我们引入了cache
属性来存储最近读取的键值对。getItem
方法首先尝试从缓存中读取数据,如果未找到,则从本地存储中读取,并将结果存入缓存。clearCache
方法允许在必要时清空缓存,例如当本地存储数据发生变更后。
适配不同环境:sessionStorage和localStorage
为了使Storage
类更加灵活,能够根据不同的需求选择使用sessionStorage
或localStorage
,我们可以增加一个配置选项:
class Storage {
constructor(useSessionStorage = false) {
if (Storage.instance) {
throw new Error('Use getInstance() to get the single instance of this class.');
}
Storage.instance = this;
this.storageType = useSessionStorage ? sessionStorage : localStorage;
this.cache = {};
}
// ...其他方法不变...
getItem(key) {
if (this.cache[key]) {
return this.cache[key];
}
const value = this.storageType.getItem(key);
this.cache[key] = value;
return value;
}
setItem(key, value) {
this.storageType.setItem(key, value);
this.cache[key] = value;
}
}
现在,通过在构造函数中传入true
,Storage
实例将使用sessionStorage
而不是默认的localStorage
。
总结
单例模式虽然在一般情况下不会使用,但在大型企业开发项目上,会很有用,了解了单例模式,就可以应对面试官的一顿拷问。
转载自:https://juejin.cn/post/7387981935009792034