#单例模式:前端面试的必考点!

77 阅读3分钟

最近在准备面试,发现单例模式几乎是每个面试官必问的设计模式。今天我就来聊聊单例模式到底是什么,为什么面试官这么爱问,以及如何用实际例子把它讲清楚。

什么是单例模式?简单来说就是“独一无二”

想象一下公司里的CEO职位——整个公司只能有一个CEO,不能同时存在两个。单例模式就是这个道理:确保一个类只有一个实例,并且提供全局访问点。

为什么需要单例?因为有些情况下,多个实例会造成资源浪费或者状态不一致。比如全局配置管理、缓存系统、登录弹窗这些场景,只需要一个实例就够了。

面试真题:实现基于LocalStorage的Storage单例

这道题我最近在面试中真的遇到过!题目要求实现一个Storage类,让它成为单例,并基于LocalStorage封装setItem和getItem方法。

先来看看代码实现

class Storage {
  // 静态属性,用于存储唯一实例
  static instance = null;

  // 获取单例实例的静态方法
  static getInstance() {
    if (!Storage.instance) {
      Storage.instance = new Storage();
    }
    return Storage.instance;
  }

  // 设置存储项
  setItem(key, value) {
    localStorage.setItem(key, JSON.stringify(value));
  }

  // 获取存储项
  getItem(key) {
    const value = localStorage.getItem(key);
    return value ? JSON.parse(value) : null;
  }
}

// 使用示例
const storage1 = Storage.getInstance();
const storage2 = Storage.getInstance();

console.log(storage1 === storage2); // true,确实是同一个实例

storage1.setItem('name', '小明');
console.log(storage2.getItem('name')); // '小明'

面试官为什么爱考这道题?

这道题看似简单,但实际上考察了很多知识点:

  1. 对单例模式的理解:是否能正确实现单例模式
  2. LocalStorage的操作:是否熟悉本地存储API
  3. 数据序列化:是否知道LocalStorage只能存字符串,需要JSON序列化
  4. 代码封装能力:是否能设计出易用的API

真实应用场景:登录弹窗的优化

单例模式不只是面试题,在实际项目中也非常有用。比如实现登录弹窗:

class LoginModal {
  static instance = null;
  
  static getInstance() {
    if (!LoginModal.instance) {
      LoginModal.instance = new LoginModal();
    }
    return LoginModal.instance;
  }
  
  constructor() {
    this.modal = this.createModal();
    this.isShowing = false;
  }
  
  createModal() {
    // 创建弹窗的DOM元素和样式
    const modal = document.createElement('div');
    modal.innerHTML = `
      <div class="modal-content">
        <h2>登录</h2>
        <form>
          <input type="text" placeholder="用户名">
          <input type="password" placeholder="密码">
          <button type="submit">登录</button>
        </form>
      </div>
    `;
    modal.style.display = 'none';
    document.body.appendChild(modal);
    return modal;
  }
  
  show() {
    this.modal.style.display = 'block';
    this.isShowing = true;
  }
  
  hide() {
    this.modal.style.display = 'none';
    this.isShowing = false;
  }
}

// 在整个应用中任何地方需要登录弹窗,都使用同一个实例
const loginModal = LoginModal.getInstance();
document.getElementById('login-btn').addEventListener('click', () => {
  loginModal.show();
});

这样做有什么好处?

  • 性能优化:推迟到第一次使用时才创建弹窗(懒加载)
  • 避免重复创建:整个应用共用同一个弹窗实例
  • 保持状态一致:无论从哪里调用,操作的都是同一个弹窗

面试中可能会问到的深入问题

  1. 懒加载 vs 饿汉式:单例模式有两种实现方式——懒加载(第一次使用时创建)和饿汉式(类加载时就创建)。JavaScript中通常使用懒加载。
  2. 线程安全:在Java等语言中,单例模式需要考虑多线程环境。虽然JavaScript是单线程的,但面试官可能会问到这个概念,以考察你的知识广度。
  3. 单例模式的缺点:全局状态难以测试、可能造成耦合度高等问题。能提到这些说明你思考全面。
  4. 其他实现方式:除了类静态方法,还可以使用闭包、模块模式等方式实现单例。