本文深入解析 JavaScript 中单例模式的实现原理,涵盖从简单全局对象到基于闭包和代理的高级写法。重点介绍如何通过解耦创建与判断逻辑优化代码结构,并探讨惰性单例在提升页面性能中的应用场景,帮助开发者掌握唯一实例的全局访问控制。
概述
单例模式(Singleton Pattern),亦称单体模式,其核心定义是确保一个类仅存在一个实例,并提供一个全局访问该实例的入口点;
在深入阅读本文之前,读者可能对单例模式的具体概念尚显模糊,但在日常前端开发实践中,其实早已频繁接触并应用了这一模式;
由于 JavaScript 语言本身不直接支持传统意义上的类定义,单例模式所强调的“唯一性”与“全局访问”特性,可自然映射到 JavaScript 的全局对象上。利用 ES6 中 let 关键字不允许重复声明的特性,全局对象恰好满足了这两个核心条件,因此,全局对象可被视为最基础形式的单例模式;
let obj = {
name:"咸鱼",
getName:function(){}
}
通过上述代码示例可知,变量 obj 即构成一个单例实例,因为它严格符合单例模式的两大核心特征:实例的唯一性以及可被全局访问;
尽管上述实现方式直观,但通常不建议在生产环境中直接采用此方法,因为滥用全局对象或全局变量会引发以下潜在问题:
1. 污染命名空间,极易导致变量名冲突;2. 维护成本增加,不当操作可能导致现有变量被意外覆盖;
代码实现
简单版单例模式
逻辑分析:由于单例要求只能存在一个实例,因此我们需要引入条件分支进行判断。若实例已存在,则直接返回该实例;若不存在,则执行新建实例的操作;
let Singleton = function(name){
this.name = name;
this.instance = null;
}
Singleton.prototype.getName = function(){
console.log(this.name);
}
Singleton.getInstance = function(name){
if(this.instace){
return this.instance;
}
return this.instance = new Singleton(name);
}
let winner = Singleton.getInstance("winner"); //winner
console.log(winner.getName());
let sunner = Singleton.getInstance("sunner"); //winner
console.log(sunner.getName())
在上面的代码实现中,我们通过检查变量 instance 的值来判断实例是否已创建。若 instance 存在,则直接返回 this.instance;若不存在,则新建实例并将其赋值给 instance 变量;
然而,上述代码仍存在缺陷,主要在于创建对象的操作与判断实例存在的逻辑紧密耦合,这违反了软件设计中的“单一职责原则”;
改良版
改进思路:借助闭包机制,将实例的判断逻辑封装起来,从而实现更清晰的代码结构;
注意:若对闭包概念尚不熟悉,建议先学习相关基础知识;
let CreateSingleton = (function(){
let instance = null;
return function(name){
this.name = name;
if(instance){
return instance
}
return instance = this;
}
})()
CreateSingleton.prototype.getName = function(){
console.log(this.name);
}
let winner = new CreateSingleton("winner"); //winner
console.log(winner.getName());
let sunner = new CreateSingleton("sunner"); //winner
console.log(sunner.getName())
代理版单例模式
通过引入代理模式,将对象的创建操作与实例存在的判断操作进行解耦和拆分,实现更细粒度的职责划分,从而更好地符合“单一职责原则”;
let ProxyCreateSingleton = (function(){
let instance = null;
return function(name){
if(instance){
return instance
}
return instance = new Singlton(name);
}
})();
let Singlton = function(name){
this.name = name;
}
Singlton.prototype.getName = function(){
console.log(this.name);
}
let winner = new ProxyCreateSingleton("winner");
console.log(winner.getName());
let sunner = new ProxyCreateSingleton("sunner");
console.log(sunner.getName());
在上面的代码结构中,ProxyCreateSingleton() 函数仅负责判断实例是否存在,而 Singlton 构造函数则专门负责对象的创建与属性赋值;
惰性单例模式
在实际开发中,我们常遇到此类场景:页面中多次触发弹窗提示,但每次提示的内容各不相同;
针对此类需求,我们可以立即联想到使用单例模式,其中弹窗即为单例实例,而提示内容则作为参数传递。此时,可采用惰性单例模式来实现该功能;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="loginBtn">有梦想的咸鱼</div>
</body>
<script>
let getSingleton = function(fn) {
var result;
return function() {
return result || (result = fn.apply(this, arguments)); // 确定this上下文并传递参数
}
}
let createAlertMessage = function(html) {
var 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('看来真的是个咸鱼');
alertMessage.style.display = 'block';
}
</script>
</html>
所谓惰性单例,指的是在页面初始加载阶段并不创建实例,而是等到用户点击页面中的特定元素(如 div)时才触发实例的创建(即按需创建)。这种策略有助于提升网页性能,加快页面渲染速度;
以上即为 JavaScript 实现单例模式的详细内容,更多关于 JavaScript 单例模式的参考资料,敬请关注本站其他相关文章!