闭包有哪些使用场景

16 阅读2分钟

闭包在JavaScript中非常实用,常见的使用场景包括:

1. 数据封装与私有变量

创建只能通过特定方法访问的私有数据:

function createCounter() {
  let count = 0; // 私有变量
  return {
    increment: function() {
      count++;
      return count;
    },
    decrement: function() {
      count--;
      return count;
    },
    getCount: function() {
      return count;
    }
  };
}

const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.count); // undefined,无法直接访问

2. 函数工厂

动态创建具有特定配置的函数:

function createMultiplier(multiplier) {
  return function(x) {
    return x * multiplier;
  };
}

const double = createMultiplier(2);
const triple = createMultiplier(3);

console.log(double(5)); // 10
console.log(triple(5)); // 15

3. 事件处理与回调函数

保持对事件上下文的引用:

function setupButton() {
  let clickCount = 0;
  
  document.getElementById('myButton').addEventListener('click', function() {
    clickCount++;
    console.log(`按钮被点击了 ${clickCount} 次`);
  });
}
// 每次点击都会记住并更新 clickCount

4. 模块模式

实现模块化代码,暴露公共接口,隐藏实现细节:

const calculator = (function() {
  let memory = 0;
  
  function add(x, y) {
    return x + y;
  }
  
  function store(value) {
    memory = value;
  }
  
  return {
    add: add,
    store: store,
    getMemory: function() {
      return memory;
    }
  };
})();

console.log(calculator.add(2, 3)); // 5
calculator.store(10);
console.log(calculator.getMemory()); // 10

5. 防抖与节流

优化高频触发的事件:

// 防抖:等待一段时间后才执行
function debounce(fn, delay) {
  let timer;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}

// 节流:固定时间执行一次
function throttle(fn, limit) {
  let inThrottle;
  return function(...args) {
    if (!inThrottle) {
      fn.apply(this, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

6. 记忆化/缓存函数

缓存计算结果,提高性能:

function memoize(fn) {
  const cache = {};
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache[key] === undefined) {
      cache[key] = fn.apply(this, args);
    }
    return cache[key];
  };
}

const memoizedFactorial = memoize(function(n) {
  if (n === 0) return 1;
  return n * memoizedFactorial(n - 1);
});

7. 循环中的闭包

解决异步循环中的变量捕获问题:

// 常见问题
for (var i = 0; i < 5; i++) {
  setTimeout(function() {
    console.log(i); // 全部输出5
  }, 100);
}

// 解决方案1:使用IIFE
for (var i = 0; i < 5; i++) {
  (function(j) {
    setTimeout(function() {
      console.log(j); // 0,1,2,3,4
    }, 100);
  })(i);
}

// 解决方案2:使用let(块级作用域)
for (let i = 0; i < 5; i++) {
  setTimeout(function() {
    console.log(i); // 0,1,2,3,4
  }, 100);
}

8. 函数柯里化

将多参数函数转换为一系列单参数函数:

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function(...nextArgs) {
        return curried.apply(this, args.concat(nextArgs));
      };
    }
  };
}

function add(a, b, c) {
  return a + b + c;
}

const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6

9. 状态管理

在函数式编程中管理状态:

function createState(initialState) {
  let state = initialState;
  
  return {
    getState: () => state,
    setState: (newState) => {
      state = typeof newState === 'function' 
        ? newState(state) 
        : newState;
    }
  };
}

注意事项

  1. 内存泄漏:闭包会阻止垃圾回收,注意解除不必要的引用
  2. 性能考量:过度使用闭包可能影响性能
  3. 调试难度:闭包可能使调试更复杂

闭包是JavaScript强大功能的体现,合理使用可以写出更优雅、模块化的代码。