前言
从这一篇开始,我会持续地更新每一种设计模式的内容,争取用通俗易懂的语言讲解和解释清楚。如果对你有帮助,请不要吝啬手中的赞~ 如果对文章内容有任何疑惑都可以在评论区提出和讨论~
本系列文章中的完整源码已上传 github 仓库,你可以在这里 FatMii/Design-Mode 获取。同样的,如果对你有帮助,请给我一个star~谢谢
设计模式合集链接:
Hello~大家好,上一篇内容讲完了工厂模式
,这一篇我们继续学习单例模式
。
什么是单例模式呢?顾名思义,就是一个类只能生成一个实例对象
,并提供一个全局访问点来获取这个实例
。这个模式在很多情况下都非常有用,特别是当你需要控制某些共享资源或服务的访问时,如状态管理,配置对象
等。
除此之外,单例模式被广泛应用于多种框架和工具包场景,对于 Vue.js
全家桶,即 Vue.js
框架及其官方维护的周边库(如 Vuex、Vue Router等)
,单例模式的应用尤为明显。
前端开发中单例模式的应用场景:
-
状态管理(Vuex) :
- Vuex :Vuex 是 Vue 的官方状态管理库,它实现了一个集中式存储,用来存储所有组件的共享状态。这个存储是响应式的,当 Vue 组件从 store 中读取状态的时候,若状态发生变化,相应的组件也会得到高效的更新。Vuex 的 store 是一个单例,整个应用
只创建一个 store 实例
,确保了应用各个部分状态的一致性。
- Vuex :Vuex 是 Vue 的官方状态管理库,它实现了一个集中式存储,用来存储所有组件的共享状态。这个存储是响应式的,当 Vue 组件从 store 中读取状态的时候,若状态发生变化,相应的组件也会得到高效的更新。Vuex 的 store 是一个单例,整个应用
-
路由管理(Vue Router) :
- Vue Router :Vue Router 是 Vue 的官方路由管理器,它允许你建立单页面应用中的路由系统。在 Vue Router 中,
整个应用只有一个 router 实例
,这个实例在不同组件之间共享,保证了路由配置的统一性和路由状态的一致性。每次路由变化,对应的视图和组件状态会更新,这需要确保全局只有一个路由实例来控制这些更新。
- Vue Router :Vue Router 是 Vue 的官方路由管理器,它允许你建立单页面应用中的路由系统。在 Vue Router 中,
单例模式的核心特点:
- 单一实例:确保只创建一个类的实例。
- 全局访问点:提供一个全局可访问的接口用于访问该实例。
- 自我管理:单例类通常自己负责管理它的创建和生命周期
单例模式的缺点:
- 全局状态:单例模式本质上提供了一个全局状态,这可能导致代码难以测试和维护,因为系统的不同部分可能会以不可预见的方式相互影响。
- 违反单一职责原则:由于单例类除了自己的方法外,还负责管理自身的唯一实例,这可能会使单例类过于复杂。
- 扩展困难:由于单例模式的特性,扩展单例类或其子类通常比较复杂,有时可能需要修改单例实现,这可能会导致代码违背开放-封闭原则。
代码实现
javascript
class Singleton {
static instance;
static getInstance() {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
constructor() {
if (Singleton.instance) {
return Singleton.instance;
}
Singleton.instance = this;
}
// 示例方法
someMethod() {
console.log("I am doing something.");
}
}
// 使用单例
const instance1 = new Singleton();
const instance2 = new Singleton();
instance1.someMethod(); // Output: I am doing something.
console.log(instance1 === instance2); // Output: true
在ES6中,通过使用class
关键字和static
静态属性,我们可以非常方便地实现单例模式
。使用static
关键字声明的属性或方法,如示例中的instance
属性和getInstance
方法,将成为类的静态属性
或静态方法
。
那么,静态属性、静态方法
与普通属性、普通方法
有何区别呢?
关键区别在于:静态属性和方法属于类本身,而普通属性和方法则属于由类构造的实例对象。
简而言之,当你在类上定义静态成员时,这些成员可直接通过类访问,而不依赖于任何实例。这使得静态成员非常适合用作实现单例模式的工具,因为它们不会随着实例的创建而重复生成。
javascript
console.log(Singleton.instance); // Singleton {}
console.log(Singleton.getInstance()) // Singleton {}
console.log(instance1.instance); // undefined,因为实例对象instance1上没有声明过普通属性instance
console.log(instance1.getInstance()); // ReferenceError: instance1 is not defined
// 静态方法调用直接在类上进行,不能在类的实例上调用。静态方法通 常用于创建实用程序函数。
关于static的更详细介绍,你可以在CDN上学习:static
ES5实现单例模式
如果没有es6中的static关键字我们想实现一个单例模式是比较繁琐的,需要借助到闭包
。
在JavaScript中使用闭包
来实现单例模式主要是为了提供几个关键的好处,包括封装性
、数据隐私
和状态持久化
。闭包允许某些数据保持私有
,而对外只暴露必要的接口,这是实现单例模式的理想选择。
javascript
var Singleton = (function() {
// 私有变量,存储单例实例
var instance;
// 构造函数定义
function Singleton() {
if (instance) {
return instance;
}
instance = this;
// 定义一些属性和方法
this.property = "I am a property";
this.method = function() {
console.log("I am a method");
};
}
return Singleton;
})();
// 创建实例
var instance1 = new Singleton();
var instance2 = new Singleton();
console.log(instance1 === instance2); // 输出: true
instance1.method(); // 输出: "I am a method"
在这个实现中,Singleton
构造函数内部检查是否已经存在一个实例。如果存在,它将直接返回这个实例;如果不存在,它将创建一个新的实例。这是通过在构造函数内部检查一个私有变量instance
来实现的,该变量在构造函数外部通过闭包保持私有。这种方法有效地确保了类的单一实例