JavaScript单例模式:静态方法与闭包实现详解
本文详解JavaScript中的单例模式,重点介绍通过static静态方法和闭包两种主流方式实现唯一实例管理。结合Storage类封装localStorage操作的完整代码示例,演示如何确保全局访问点及数据一致性,帮助开发者理解并应用单例模式优化内存与状态管理。
单例模式的核心概念
从字面理解,“单”代表唯一,“例”指代实例。单例模式的核心目标是确保一个类在整个程序生命周期中仅存在一个实例。若尝试再次创建该类的实例,系统应抛出错误或直接返回已存在的第一个实例,从而严格控制对象数量。
在JavaScript中,当我们使用 new 关键字实例化构造函数时,其底层执行机制遵循特定步骤,具体原理如下文所述。
function Person() {
// let obj = { // 1
// name: 'Tom' // 3的效果
// }
// Person.call(obj) // 2 让Person构造函数的this指向obj
// obj.__proto__ = Person.prototype // 4 //让实例对象的隐式原型等于构造函数的显示原型
// return obj // 5 返回实例对象
this.name = 'Tom' // 3 往obj上添加属性
let p1 = new Person()
let p2 = new Person()
console.log(p1 === p2);//false
}
使用 new 关键字创建对象时,每次操作都会生成一个全新的实例。这意味着每个实例都拥有独立的内存空间及唯一的引用地址。因此,即便两个对象源自同一个构造函数,它们的引用地址依然不同,彼此独立。
单例模式的本质,就是打破常规的对象创建机制,确保无论调用多少次构造函数或实例化方法,最终返回的都是同一个对象引用。
实现方案
方案一:利用static静态方法
静态方法直接定义在类本身而非其实例上,因此它不依赖于类的具体实例。这意味着静态方法只能通过类名直接调用,无法通过实例对象访问。
全局访问:静态方法提供了一个类级别的全局入口,允许开发者在不实例化类的情况下直接调用该方法。
实例管理:静态方法可以通过操作静态属性来存储和管理唯一实例,从而在逻辑上确保实例的唯一性。
具体代码实现如下:
class Person {
constructor() {
this.name = 'Tom' //初始化name属性
}
static getInstance() {//静态方法,用于创建或获取 Person的唯一实例
if (!Person.instance) {//如果不存在,则创建一个新的 实例
Person.instance = new Person()
}
return Person.instance//将其赋值给Person.instance
}
}
let p1 = Person.getInstance()
let p2 = Person.getInstance()
console.log(p1 === p2)//true

方案二:利用闭包
1. 闭包基础概念
闭包是指一个函数与其周围状态(词法环境)的引用捆绑在一起形成的组合。这个环境包含了该函数定义时作用域内的所有变量。
在JavaScript中,闭包允许函数访问其外部作用域中的变量。即使函数在外部作用域之外被调用,它依然保留对这些变量的访问权限,从而维持状态。
2. 单例模式的需求分析
实现单例模式需满足以下核心特性:
- 唯一实例:确保在整个应用程序运行期间,该类仅存在一个实例对象。
- 全局访问:提供一个统一的全局接口或方法,用于获取这个唯一的实例。
3. 使用闭包实现单例模式
闭包可以通过封装实例的创建与管理逻辑来实现单例模式。具体而言,闭包能够创建一个私有作用域来保存唯一实例,从而严格控制实例的创建过程,确保其唯一性。
相关代码示例如下:
class Person {
constructor() {
this.name = 'Tom'
}
static getInstance() {
let instance = null
return function() {
if (!instance) {
instance = new Person()
}
return instance
}
}
}
const simple = Person.getInstance()
let p1 = simple()
let p2 = simple()
console.log(p1 === p2);

上述代码中关于闭包的关键机制解析:
- 每次调用返回的匿名函数时,闭包会持续记住
instance变量的当前值。 - 如果
instance的值为null,则创建一个新的Person实例,并将其赋值给instance变量。 - 如果
instance已经存在(非空),则直接返回该已存在的实例。 - 闭包创建了一个私有作用域,在此作用域内管理
instance变量,使其无法被外部代码直接访问或修改。这确保了只有一个实例能够被创建,并且所有对该实例的访问都通过同一个方法完成。
实际应用场景
以下通过一段 HTML 代码进行演示,展示如何利用单例模式来管理浏览器的 localStorage 数据存取。在某些业务场景下,我们需要确保对 localStorage 的操作始终由同一个实例执行,以避免重复创建对象,从而保证数据的一致性与操作的原子性。
<body>
<button id="save">存储</button>
<button id="get">取值</button>
<script>
let save = document.getElementById('save')
let get = document.getElementById('get')
// 定义一个Storage类,用单例模式封装LocalStorage操作
class Storage {
// 使用静态方法实现单例模式
static getInstance() {
if (!Storage.instance) {
Storage.instance = new Storage();
}
return Storage.instance;
}
// 获取LocalStorage中的数据
getItem(key) {
return localStorage.getItem(key);
}
// 存储数据到LocalStorage
setItem(key, val) {
return localStorage.setItem(key, val);
}
}
// 获取唯一的Storage实例
const storage = Storage.getInstance();
const storage2 = Storage.getInstance();
// 点击存储按钮,保存数据到LocalStorage
save.onclick = function() {
storage.setItem('name', '高老师');
}
// 点击取值按钮,弹出LocalStorage中的数据
get.onclick = function() {
alert(storage2.getItem('name'));
}
</script>
</body>
</html>
代码逻辑解读
HTML 结构部分: 页面中包含两个按钮,分别用于执行“存储”和“取值”操作。通过
id属性获取对应的按钮 DOM 元素,并为其绑定相应的点击事件监听器。Storage 类的定义:
Storage类封装了对localStorage的具体操作,并通过静态方法getInstance实现单例模式。该方法确保Storage类在整个应用生命周期中仅有一个实例,使得所有对localStorage的访问都统一通过该对象进行。按钮事件处理逻辑:
- 当点击“存储”按钮时,
Storage实例的setItem方法将'高老师'数据存储在localStorage中,使用的键名为'name'。 - 当点击“取值”按钮时,
Storage实例的getItem方法从localStorage中读取存储在'name'键下的数据,并通过弹窗将结果展示给用户。
- 当点击“存储”按钮时,
结果验证与分析
在上述代码运行过程中,无论用户点击“存储”或“取值”按钮多少次,所有的操作均通过同一个 Storage 实例执行。这保证了数据的一致性,并有效利用单例模式避免了不必要的对象重复创建。
应用优势总结
通过这种封装方式,我们可以更便捷地管理和扩展与 localStorage 相关的操作,同时避免了全局状态可能带来的混乱。此外,单例模式的应用确保了在整个应用程序中仅存在一个 Storage 实例,这不仅简化了管理复杂度,还有效减少了内存消耗。
以上即为JavaScript设计模式之单例模式的详细解析,更多关于JavaScript单例模式的深入资料,欢迎查阅本站其他相关文章。
什么时候该用JS构造器:与工厂模式、字面量的选择对比
2026-11-05 16:18:22
推荐几款轻量级JS照片墙开源库及对比
2026-11-05 15:56:19
JavaScript递归写法有哪些常见陷阱及避免方法
2026-11-05 14:53:38
JS数组浅拷贝与深拷贝怎么选?场景与方法总结
2026-11-05 14:41:02
p5.js 中 setup() 和 draw() 函数的执行机制详解
2026-11-05 12:32:38
