JS闭包不理解?一杯咖啡的时间带你轻松理解闭包!

一、为什么闭包很重要?

闭包是JavaScript的灵魂级概念!无论是:

  • 面试高频考点(10个面试9个问)
  • 日常开发(回调函数、模块化、防抖节流)
  • 框架原理(React Hooks、Vue Composition API)

不懂闭包 = 学不好JS

接下来用最通俗的方式帮你彻底掌握!


闭包的优缺点

优点 缺点
封装私有变量,增强数据安全性 过度使用可能导致内存泄漏
实现高阶函数(如柯里化、装饰器) 闭包变量无法被垃圾回收机制释放
解决循环中的异步问题 可能影响代码可读性

二、闭包的生活化比喻

想象你有一个魔法背包🎒:

  1. 装东西:把书本(变量)放进背包(外层函数)
  2. 交给朋友:把背包交给朋友保管(返回内层函数)
  3. 随时取用:朋友即使在你离开后(外层函数执行完毕),仍能打开背包使用书本(访问变量)

三要素

👉 背包里的物品 = 外部函数的变量

👉 获得背包的朋友 = 内部函数

👉 魔法契约 = 闭包规则


三、闭包的双层定义

官方版 :函数能够访问并记住其词法作用域,即使该函数在父级作用域外执行
人话版:内部函数随身携带出生地(外层函数)的变量,走到哪里都能用


四、闭包的核心原理

1. 词法作用域(出生决定论)

javascript 复制代码
function outer() {
    const phone = "iPhone15"; // 出生时自带的手机
    function inner() {
        console.log(phone); // 永远记得自己出生时有什么
    }
    return inner;
}
  • 函数作用域在定义时确定,和在哪里执行无关
  • 内部函数像婴儿记得母亲(外部函数)的样貌

2. 垃圾回收机制(生存法则)

javascript 复制代码
function outer() {
    const data = "重要资料";
    return function() { 
        console.log(data) 
    };
}

const func = outer(); // 执行完毕
func(); // 仍然能打印"重要资料"
  • JS会自动清除不再使用的变量
  • 闭包会死死抓住用到的变量,不让系统回收

五、闭包实战训练营

案例1:私人保险箱

javascript 复制代码
function createSafe() {
    let money = 0; // 保险箱里的钱,外人无法直接接触

    return {
        deposit: (amount) => {
            money += amount;
            console.log(`存入${amount},余额${money}`);
        },
        checkBalance: () => {
            console.log(`当前余额:${money}`);
        }
    };
}

const mySafe = createSafe();
mySafe.deposit(100); // 存入100,余额100
mySafe.checkBalance(); // 当前余额:100
// 无法直接修改money变量!

闭包部分 :返回的depositcheckBalance方法形成了闭包,锁住了money变量

案例2:网页点击统计

javascript 复制代码
function setupCounter() {
    let count = 0;
    const btn = document.getElementById('myBtn');
    
    btn.addEventListener('click', function() {
        count++;
        console.log(`这是第${count}次点击`);
    });
}
setupCounter();

闭包在哪里 :点击回调函数记住了count变量,即使setupCounter早已执行完毕


六、必知常见问题

Q1:闭包必须return函数吗?

不一定! 只要内部函数逃逸到外部环境,就会形成闭包:

javascript 复制代码
// 情况1:定时器
function startTimer() {
    const startTime = Date.now();
    setInterval(() => {
        console.log(`已运行${Date.now() - startTime}ms`);
    }, 1000);
}

// 情况2:DOM事件
function trackScroll() {
    const scrollY = window.scrollY;
    window.addEventListener('scroll', () => {
        console.log(`当前滚动位置:${scrollY}`);
    });
}

Q2:闭包会导致内存泄漏吗?

不一定!现代浏览器很聪明

只有当闭包长期持有不再需要的大数据时才会泄漏:

javascript 复制代码
// 危险操作!
function leakMemory() {
    const hugeData = new Array(1000000).fill('⚠️'); // 1MB数据
    return function() {
        console.log('这个闭包根本用不到hugeData!');
    };
}

const func = leakMemory(); // hugeData被无意义地保留着

安全操作指南

javascript 复制代码
// 正确做法:及时清理
let usefulClosure = createSafe();
// 使用完毕后...
usefulClosure = null; // 斩断引用链

// 推荐做法:避免保留无用数据
function safeExample() {
    const usefulData = "需要的数据";
    const tempData = "临时数据";
    
    return function() {
        // 只使用usefulData
        console.log(usefulData);
        // tempData在闭包形成后可以清理
        tempData = null; // 手动释放
    };
}

七、闭包使用口诀

  1. 需要长期存数据 → 大胆用闭包
  2. 不再使用的闭包 → 及时设null
  3. 大对象要小心 → 用完就清理
  4. 避免过度使用 → 按需取用

现在你已经是闭包小达人了!试着写个累计计算器练练手吧~ 🚀

遇到问题别担心,多写多调试才是硬道理!

相关推荐
炫饭第一名4 小时前
速通Canvas指北🦮——基础入门篇
前端·javascript·程序员
over6975 小时前
从 LLM 到全栈 Agent:MCP 协议 × RAG 技术如何重构 AI 的“做事能力”
面试·llm·mcp
进击的尘埃5 小时前
Vue3 响应式原理:从 Proxy 到依赖收集,手撸一个迷你 reactivity
javascript
None3215 小时前
【NestJs】基于Redlock装饰器分布式锁设计与实现
后端·node.js
willow5 小时前
JavaScript数据类型整理1
javascript
LeeYaMaster5 小时前
20个例子掌握RxJS——第十一章实现 WebSocket 消息节流
javascript·angular.js
UIUV6 小时前
RAG技术学习笔记(含实操解析)
javascript·langchain·llm
SuperEugene6 小时前
Vue状态管理扫盲篇:如何设计一个合理的全局状态树 | 用户、权限、字典、布局配置
前端·vue.js·面试
颜酱8 小时前
理解二叉树最近公共祖先(LCA):从基础到变种解析
javascript·后端·算法
Sailing8 小时前
🚀 别再乱写 16px 了!CSS 单位体系已经进入“计算时代”,真正的响应式布局
前端·css·面试