JavaScript 单例模式实现:从全局对象到惰性创建

作者:青柠

时间:2026-10-24

浏览:234

本文深入解析 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 单例模式的参考资料,敬请关注本站其他相关文章!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章