【前端学习】背闭包快疯了?我用实际场景搞懂了!

🔥 为啥写这个?

最近疯狂背八股,闭包概念懂了,但一到实际开发就懵... 昨天写组件终于悟了!分享给同被折磨的友友~

一、闭包是啥?

简单说就是:函数嵌套时,内层函数能记住外层函数的变量,哪怕外层执行完了

举个🌰:写一个延迟计数器(点击按钮,1 秒后数字 +1 ),用闭包实现:

js 复制代码
function createCounter() {
    let count = 0; 
    return () => { 
        setTimeout(() => { 
            count++; // 内层函数"记住"count,每次调用持续累加 console.log(count);
        }, 1000);
    };
} 
    
    const add = createCounter(); 
    add(); // 1秒后输出 1 
    add(); // 1秒后输出 2(闭包保留了 count=1 的状态)

二、实际开发踩坑:数据更新不生效

场景:延迟更新页面数据

假设做一个按钮,点击后 1 秒更新页面显示的数字,直接用闭包会遇到 "数据改了,但页面没变化" 的问题:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>闭包踩坑示例</title>
</head>
<body>
  <button id="btn">延迟+1</button>
  <div id="result">count: 0</div>

  <script>
    let count = 0; 
    const btn = document.getElementById('btn');
    const result = document.getElementById('result');

    btn.addEventListener('click', () => {
      setTimeout(() => {
        count++; 
        result.innerText = `count: ${count}`; // 手动更新 DOM!
      }, 1000);
    });
  </script>
</body>
</html>

为啥坑?

  • 纯 JS 开发时,普通变量修改后不会自动更新 DOM ,必须手动操作 innerText/innerHTML
  • 如果用框架(如 Vue/React),不配合响应式语法,也会遇到类似问题!

三、正确姿势:结合开发场景适配

方案 1:手动更新 DOM(纯 JS 开发)

html 复制代码
<script>
  let count = 0; 
  const btn = document.getElementById('btn');
  const result = document.getElementById('result');

  btn.addEventListener('click', () => {
    setTimeout(() => {
      count++; 
      // 关键:手动更新 DOM,让页面变化
      result.innerText = `count: ${count}`; 
    }, 1000);
  });
</script>

核心逻辑 :JS 不自动监听变量变化,修改数据后必须手动操作 DOM 才能更新页面。

方案 2:配合框架响应式(以 React 为例)

React 中,用 useState 管理状态,闭包捕获 setState 来更新:

jsx 复制代码
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0); 

  const handleClick = () => {
    setTimeout(() => {
      // 用 setCount 触发响应式更新
      setCount(prev => prev + 1); 
    }, 1000);
  };

  return (
    <div>
      <button onClick={handleClick}>延迟+1</button>
      <div>count: {count}</div> 
    </div>
  );
}

避坑点

  • 直接修改 count(如 count++ )不会触发更新,必须用 setState
  • 闭包若捕获旧的 count 值,会导致 "状态不更新"(俗称 stale closure 问题),用函数式更新(prev => prev + 1 )可解决。

四、闭包必用场景(开发高频需求)

场景 1:搜索框防抖(避免频繁请求)

js 复制代码
// 用闭包保存定时器 ID,防止重复触发
const debounce = (fn, delay) => {
  let timer = null; // 闭包变量,多次调用不会重置
  return function() {
    if (timer) clearTimeout(timer); 
    timer = setTimeout(() => {
      fn.apply(this, arguments);
    }, delay);
  };
};

// 实际用:监听输入框变化
const input = document.getElementById('search-input');
input.addEventListener('input', debounce((e) => {
  console.log('发请求搜索:', e.target.value);
}, 500));

踩坑点:如果不用闭包,每次输入都会重置定时器,防抖功能直接失效!

场景 2:模块私有变量(避免全局污染)

js 复制代码
//  utils/counter.js
const createCounter = () => {
  let count = 0; // 闭包变量,外部无法访问
  return {
    increment: () => count++,
    getCount: () => count
  };
};

// 其他文件引入
import { createCounter } from './utils/counter.js';

const counter = createCounter();
counter.increment();
console.log(counter.getCount()); // 输出 1(外部无法直接改 count)

价值:做工具库 / 组件时,用闭包实现 "私有状态",防止全局变量污染。

五、避坑总结:闭包开发关键提醒

开发场景 正确姿势 常见错误
纯 JS 开发 修改数据后手动更新 DOM 只改变量,页面不变化
React 开发 useState/ 函数式更新 直接修改状态,不触发更新
工具库开发 用闭包封装私有变量 全局变量污染逻辑

六、总结:闭包该咋用?

✅ 场景:需要 "记住" 变量状态时(比如防抖函数、组件私有变量)

❌ 踩坑:和 React 响应式结合时,注意用useState包裹

💡 小技巧:写闭包后,检查变量是 "值传递" 还是 "引用传递"

最后碎碎念

背八股真的好痛苦... 但把知识点砸到项目里,突然就通了! 你们遇到过这些坑吗?

  • 用闭包写防抖,第一次点击没生效?(定时器逻辑没处理好!)
  • React 里遇到 "陈旧闭包",状态永远是旧的?
  • 有没有不用闭包也能实现类似功能的奇技淫巧?
相关推荐
web前端1231 小时前
# 多行文本溢出实现方法
前端·javascript
人间观察员1 小时前
如何在 Vue 项目的 template 中使用 JSX
前端·javascript·vue.js
EndingCoder1 小时前
安装与环境搭建:准备你的 Electron 开发环境
前端·javascript·electron·前端框架
Lucky_Turtle1 小时前
【electron】一、安装,打包配置
javascript·arcgis·electron
Running_slave1 小时前
Web跨标签页通信应该怎么玩?
javascript·css·后端
雪中何以赠君别2 小时前
Vue 2 与 Vue 3 双向绑定 (v-model) 区别详解
前端·javascript·vue.js
林太白2 小时前
Vue3-ElementPlus使用
前端·javascript·vue.js
赵民勇2 小时前
如果已经安装了electron的一个版本,再次使用命令npm install electron不指定electron版本时,会下载安装新版本么?
javascript·electron·npm
支撑前端荣耀2 小时前
优雅的Git提交:用Husky为你的项目加上提交约束
前端·javascript
林太白2 小时前
npm多组件发布Vue3+TS版本,快来像Antd一样搭建属于你的UI库吧
前端·javascript·node.js