本文深入解析 JavaScript 中的单例模式,涵盖创建型设计模式的核心概念。通过字面量、类封装及工厂方法三种方式展示具体实现,重点讲解 new 关键字在单例中的行为特性及不可变对象的应用,帮助开发者掌握控制全局状态的最佳实践。
本文详细解析 JavaScript 设计模式中的单例模式,包括其定义与多种实现方式,供开发者参考学习。
优秀的设计模式能显著提升代码可读性,降低系统复杂度与维护成本。笔者计划通过系列文章,通俗讲解常见且实用的设计模式。
本次从最基础的模式开始:单例模式。
示例代码采用 ES6 语法编写,旨在简化非核心细节。
概念
单例模式(Singleton)归类为创建型设计模式,其核心在于限制只能创建单一对象或某个类的唯一实例。
通常使用该模式旨在控制整个应用程序的状态。在实际开发中,常见的单例应用包括:Vuex 中的 Store、Vue 的根实例,以及导出单个对象的 ES6 模块等。
字面量写法
最基础的单例实现形式如下所示。
const cat = {
name: 'mi',
age: 4
}
熟悉 const 语法的开发者应当知晓,该对象本身不可被重新赋值,但其内部属性依然可变。
若需创建不可变的单例对象,可采用以下方案。
const cat = {
name: 'mi',
age: 4
}
Object.freeze(cat);
此时无法新增或修改该对象上的任何属性,使其成为真正的“冻结”对象。
若在模块中使用上述写法,虽不会污染全局作用域,但直接生成固定对象的方式缺乏足够的灵活性。
常用写法
相比之下,使用类或工厂方法实现单例更为常见。假设存在一个名为 Logger 的类,其具备与 Console 相同的 API。
类单例
类的单例写法应用广泛,若期望按如下方式使用:
const logger = new Logger();
logger.log('msg');
// 这里大概写了 1000 行代码
const logger2 = new Logger();
logger.log('new msg');
logger === logger2; // true
即使多次 new Logger,返回的始终是同一个实例。
接下来直接展示最实用的实现方式。
class Logger {
constructor () {
if (!Logger._singleton) {
Logger._singleton = this;
}
return Logger._singleton;
}
log (...args) {
console.log(...args);
}
}
export default Logger;
上述实现将单例对象存储于构造器上,因此无论 new Logger 多少次,返回的均为同一个 Logger 实例。
此处需注意一个细节:若 new 关键字后的构造函数显式返回一个对象,new 表达式将返回该对象而非新实例。
具体原理可参考《你不知道的 JavaScript(上卷)》中关于 new 绑定的章节。
工厂单例
若不使用 new 关键字,可通过工厂方法返回单例对象。
let logger = null
class Logger {
log (...args) {
console.log(...args);
}
}
function createLogger() {
if (!logger) {
logger = new Logger();
}
return logger;
}
export default createLogger;
上述代码相当于在模块内部缓存了 logger 实例,并导出一个工厂方法。这种写法在模块化代码中较为常见,工厂方法亦可接收参数以初始化单例对象。
今日内容易于理解,其中介绍的单例写法也是笔者常用的方案。
下一篇将具体讲解工厂模式的应用。
参考内容
- 《JavaScript 设计模式》
- 《JavaScript 面向对象编程指南》
- 《你不知道的 JavaScript(上卷)》
- Working with Singletons in JavaScript
感兴趣的朋友可使用在线 HTML/CSS/JavaScript 代码运行工具:http://tools.jb51.net/code/HtmlJsRun 测试上述代码运行效果。
更多关于 JavaScript 相关内容感兴趣的读者可查看本站专题:《javascript 面向对象入门教程》、《JavaScript 错误与调试技巧总结》、《JavaScript 数据结构与算法技巧总结》、《JavaScript 遍历算法与技巧总结》及《JavaScript 数学运算用法总结》
希望本文所述对大家 JavaScript 程序设计有所帮助。