闭包与单例模式面试题解析

45 阅读2分钟

1. 单例模式的经典实现

首先,来看一道关于单例模式的经典面试题:

const getSingleton = (function() {
  let instance; // 闭包中的私有变量,保存唯一实例

  class Singleton {
    constructor(name) {
      this.name = name;
    }

    sayName() {
      console.log(this.name);
    }
  }

  // 返回获取实例的函数
  return function(name) {
    if (!instance) {
      instance = new Singleton(name);
    }
    return instance;
  };
})();

// 测试:多次调用,得到同一个实例
const s1 = getSingleton('实例1');
const s2 = getSingleton('实例2');

console.log(s1 === s2); // true(证明是同一个实例)
s1.sayName(); // 输出"实例1"(第二次调用的参数不生效,因为实例已存在)

解析:
通过闭包将 instance 变量私有化,确保无论调用多少次 getSingleton,都只会返回同一个实例。这是前端面试中常见的单例模式实现方式。


2. 商铺价格与预算的高效查找

题目描述

给定一个商铺价格数组 prices 和一个预算金额 budget,请编写一个函数,返回有多少个商铺的价格 ≤ budget。
假设 prices 数组可能非常大(长度超过 10 万),需要考虑性能优化。

例如:

const prices = [1, 4, 2, 3, 5];
console.log(findAffordableShops(prices, 3)); // 输出3(价格≤3的商铺有3家)

朴素解法

最直接的思路是遍历数组,统计价格小于等于 budget 的商铺数量,时间复杂度为 O(n):

function findAffordableShops(prices, budget) {
  let count = 0;
  for (let price of prices) {
    if (price <= budget) count++;
  }
  return count;
}

优化思路

如果多次查询不同的 budget,每次都遍历数组效率较低。可以先对数组排序,然后用二分查找,时间复杂度降为 O(n log n)(排序)+ O(log n)(查找)。

二分查找实现:

function binarySearch(arr, target) {
  let left = 0, right = arr.length - 1;
  while (left <= right) {
    let mid = Math.floor((left + right) / 2);
    if (arr[mid] === target) {
      return mid;
    } else if (arr[mid] > target) {
      right = mid - 1;
    } else {
      left = mid + 1;
    }
  }
  return left;
}

排序放在哪里最合适?

  • 每次查找都排序?
    不划算,O(n log n) > O(n),还不如直接遍历。

  • 全局排序?
    虽然高效,但会污染全局变量,不推荐。

  • 闭包+单例模式?
    用闭包缓存排序结果,既不污染全局,又能保证只排序一次,后续查找直接用缓存,效率高。

最优实现

function findAffordableShops(arr, target) {
  let sortedArr = null;
  return function() {
    if (!sortedArr) {
      sortedArr = [...arr].sort((a, b) => a - b);
    }
    // 找到第一个大于 target 的位置,该位置即为价格 ≤ target 的商铺数量
    return binarySearch(sortedArr, target + 1);
  }
}

const prices = [1, 4, 2, 3, 5];
const findShops = findAffordableShops(prices, 3);
console.log(findShops()); // 输出3

总结:

  • 单例模式和闭包结合,可以优雅地缓存数据,避免重复计算和全局污染。
  • 面对大数据量和多次查询场景,排序+二分查找+闭包缓存是高效且优雅的解法。