【JS】什么是单例模式

哎,写这玩意就得白话一段,初学js时,只知道写出来的代码能实现出来自己想要的东西,自己就不会过多的去关注其他,但随着做开发时间长了,自己也想去开发点东西,或者去理解一些其他人的代码时,总会有着不清楚,看不懂的尴尬。
初听单例模式时,还是在一次面试,面试官问什么是单例模式,大脑直接宕机了,单例模式,听说过工厂模式,没听说过单例模式啊,尴尬啊,现在其实也不清楚。
趁着这个机会,就一起学习一下,整理一下单例模式,让它彻底转化为我之利刃,为我们所用。

文章目录

什么是单例模式?

单例模式是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个唯一的实例。

您可以把它想象成:

  • 国家的总统:一个国家在任期内只有一位总统。
  • 电脑的回收站:无论你从哪个磁盘分区删除文件,打开的都是同一个回收站。
  • 任务管理器:一个系统中的任务管理器实例只能有一个,这样才能方便系统去统筹所有的任务。

为什么要用?

在一些场景的前提下,我们需要某个对象,在全局的范围中,有且仅有一个对象实例的存在,这样方便我们全局的访问以及管理。

如果创建了多个实例,会导致一些我们所不期望的问题:

  • 资源浪费
  • 数据不一致
  • 程序执行不统一

特点

优点:

  • 保证一个类只有一个实例
  • 提供全局访问点
  • 避免重复创建对象,节省内存

缺点:

  • 违反单一职责原则
  • 可能产生隐藏的依赖关系
  • 不利于单元测试(难以模拟)

关键实现要点

要实现一个单例,通常需要做到以下两点:

  1. 私有化构造函数 :防止外部使用 new 关键字随意创建多个实例。
  2. 提供一个静态方法 (如 getInstance),该方法负责检查实例是否已经存在:
    • 如果已存在,则返回这个已有的实例。
    • 如果不存在,则创建一个新的实例并返回,同时将其保存下来以备下次使用。

实现方式

1.对象字面量
javascript 复制代码
const Singleton = {
    instanceId:Math.random(),//使用instanceid作为单例的唯一标志
     config: {
        apiUrl: 'https://api.example.com',
        timeout: 5000
    },
    
    getData() {
        console.log('Getting data from:', this.config.apiUrl);
    },
    
    updateConfig(newConfig) {
        this.config = { ...this.config, ...newConfig };
    }
}

// 使用
Singleton.getData();
Singleton.updateConfig({ timeout: 3000 });
2.闭包实现
javascript 复制代码
const Singleton = (function() {
    let instance;
    
    function createInstance() {
        const object = {
            counter: 0,
            increment() {
                this.counter++;
                console.log('Counter:', this.counter);
            },
            getCounter() {
                return this.counter;
            }
        };
        return object;
    }
    
    return {
        getInstance() {
            if (!instance) {
                instance = createInstance();
            }
            return instance;
        }
    };
})();

// 使用
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();

instance1.increment(); // Counter: 1
instance2.increment(); // Counter: 2
console.log(instance1 === instance2); // true
3.Class实现
javascript 复制代码
class Singleton {
    static instance;
    
    constructor() {
        if (Singleton.instance) {
            return Singleton.instance;
        }
        
        this.data = [];
        this.createdAt = new Date();
        Singleton.instance = this;
    }
    
    addItem(item) {
        this.data.push(item);
    }
    
    getItems() {
        return this.data;
    }
    
    static getInstance() {
        if (!Singleton.instance) {
            Singleton.instance = new Singleton();
        }
        return Singleton.instance;
    }
}

// 使用
const instance1 = new Singleton();
const instance2 = new Singleton();
const instance3 = Singleton.getInstance();

console.log(instance1 === instance2); // true
console.log(instance1 === instance3); // true

instance1.addItem('item1');
console.log(instance2.getItems()); // ['item1']
模块模式
js 复制代码
// configManager.js
let instance = null;
let config = {};

class ConfigManager {
    constructor() {
        if (instance) {
            return instance;
        }
        
        this.defaultConfig = {
            theme: 'dark',
            language: 'zh-CN',
            apiBaseUrl: 'https://api.example.com'
        };
        instance = this;
    }
    
    setConfig(newConfig) {
        config = { ...this.defaultConfig, ...newConfig };
    }
    
    getConfig(key) {
        return key ? config[key] : config;
    }
    
    reset() {
        config = { ...this.defaultConfig };
    }
}

export default new ConfigManager();

// 使用
import configManager from './configManager.js';

configManager.setConfig({ theme: 'light' });
console.log(configManager.getConfig('theme')); // 'light'

适用场景

  • 全局配置对象
  • 缓存系统
  • 日志记录器
  • 数据库连接池
  • 状态管理器

实际应用场景例子

全局状态管理
javascript 复制代码
class GlobalState {
    static instance;
    
    constructor() {
        if (GlobalState.instance) {
            return GlobalState.instance;
        }
        
        this.state = {};
        this.listeners = new Map();
        GlobalState.instance = this;
    }
    
    setState(key, value) {
        this.state[key] = value;
        this.notify(key, value);
    }
    
    getState(key) {
        return this.state[key];
    }
    
    subscribe(key, callback) {
        if (!this.listeners.has(key)) {
            this.listeners.set(key, new Set());
        }
        this.listeners.get(key).add(callback);
        
        // 返回取消订阅函数
        return () => {
            this.listeners.get(key)?.delete(callback);
        };
    }
    
    notify(key, value) {
        this.listeners.get(key)?.forEach(callback => {
            callback(value);
        });
    }
}

// 使用
const stateManager = new GlobalState();

// 订阅状态变化
const unsubscribe = stateManager.subscribe('user', (user) => {
    console.log('User updated:', user);
});

stateManager.setState('user', { name: 'John', age: 30 });
缓存管理器
javascript 复制代码
class CacheManager {
    static instance;
    
    constructor() {
        if (CacheManager.instance) {
            return CacheManager.instance;
        }
        
        this.cache = new Map();
        this.ttl = new Map(); // 生存时间
        CacheManager.instance = this;
    }
    
    set(key, value, ttlMs = 60000) {
        this.cache.set(key, value);
        this.ttl.set(key, Date.now() + ttlMs);
        
        // 自动清理过期缓存
        setTimeout(() => {
            if (this.ttl.get(key) <= Date.now()) {
                this.delete(key);
            }
        }, ttlMs);
    }
    
    get(key) {
        const expiry = this.ttl.get(key);
        if (expiry && expiry <= Date.now()) {
            this.delete(key);
            return null;
        }
        return this.cache.get(key);
    }
    
    delete(key) {
        this.cache.delete(key);
        this.ttl.delete(key);
    }
    
    clear() {
        this.cache.clear();
        this.ttl.clear();
    }
}

// 使用
const cache = new CacheManager();
cache.set('user-data', { id: 1, name: 'Alice' }, 5000);

setTimeout(() => {
    console.log(cache.get('user-data')); // null (已过期)
}, 6000);
日志管理器
javascript 复制代码
const Logger = (function() {
    let instance;
    let logs = [];
    
    class LoggerClass {
        constructor() {
            if (instance) {
                return instance;
            }
            instance = this;
        }
        
        log(level, message) {
            const logEntry = {
                timestamp: new Date().toISOString(),
                level,
                message
            };
            logs.push(logEntry);
            console[level](`[${logEntry.timestamp}] ${message}`);
        }
        
        info(message) {
            this.log('info', message);
        }
        
        error(message) {
            this.log('error', message);
        }
        
        warn(message) {
            this.log('warn', message);
        }
        
        getLogs() {
            return [...logs];
        }
        
        clear() {
            logs = [];
        }
    }
    
    return new LoggerClass();
})();

// 使用
Logger.info('Application started');
Logger.error('Something went wrong');
console.log(Logger.getLogs());
全局事件总线
javascript 复制代码
class EventBus {
    static instance;
    
    constructor() {
        if (EventBus.instance) {
            return EventBus.instance;
        }
        
        this.events = {};
        EventBus.instance = this;
    }
    
    on(event, callback) {
        if (!this.events[event]) {
            this.events[event] = [];
        }
        this.events[event].push(callback);
    }
    
    emit(event, data) {
        if (this.events[event]) {
            this.events[event].forEach(callback => callback(data));
        }
    }
}

// 不同模块间通信
const eventBus = new EventBus();

// 用户模块
eventBus.on('userLogin', (user) => {
    console.log('用户登录:', user.name);
});

// 购物车模块
eventBus.on('userLogin', (user) => {
    console.log('加载用户购物车');
});

总结

特点 描述
目的 控制实例数目,节省资源,保证全局一致性。
核心 一个类只有一个实例。
实现 私有构造器 + 静态获取方法。
用途 全局配置、日志记录、数据库连接池、对话框、缓存系统等。

希望这个解释能帮助您彻底理解单例模式!它是一个在软件开发中非常基础且实用的模式,同时,JavaScript 的单例模式相比其他语言更灵活。

相关推荐
linux kernel3 小时前
第二十三讲:特殊类和类型转换
开发语言·c++
笨蛋少年派3 小时前
JAVA基础语法
java·开发语言
渡我白衣3 小时前
深入剖析:boost::intrusive_ptr 与 std::shared_ptr 的性能边界和实现哲学
开发语言·c++·spring
爱吃小胖橘3 小时前
Lua语法
开发语言·unity·lua
怀旧,3 小时前
【C++】26. 智能指针
开发语言·c++·算法
Aevget3 小时前
PHP智能开发工具PhpStorm v2025.2全新上线——支持PHPUnit 12等
开发语言·ide·php·phpstorm
东方芷兰4 小时前
JavaWeb 课堂笔记 —— 20 SpringBootWeb案例 配置文件
java·开发语言·笔记·算法·log4j·intellij-idea·lua
许商4 小时前
【stm32】bash自动配置buildenv
开发语言·bash
reembarkation4 小时前
自定义分页控件,只显示当前页码的前后N页
开发语言·前端·javascript