�������� JavaScript �հ�����ԭ����ʵս

深入理解 JavaScript 闭包:从原理到实战

闭包是 JavaScript 中最核心也最容易让人困惑的概念之一。本文通过通俗的语言和实际例子,帮你彻底搞懂它。

什么是闭包?

闭包(Closure) 是指一个函数能够访问其外部作用域中变量的能力------即使这个外部函数已经执行完毕。

简单来说:函数 + 它所记住的外部变量 = 闭包

一个最简单的例子

javascript 复制代码
function makeCounter() {
  let count = 0;
  return function() {
    count++;
    return count;
  };
}

const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

makeCounter 执行完后,count 变量并没有消失。内部的匿名函数"记住"了它所在的作用域,每次调用都能访问并修改 count,这就是闭包。

闭包的核心原理:作用域链

JavaScript 采用词法作用域(Lexical Scoping),函数的作用域在定义时就确定了,而不是调用时。每个函数在创建时都会保存一个对其外部作用域的引用,这个引用就是闭包的本质。

常见使用场景

1. 数据私有化(模拟私有变量)

javascript 复制代码
function createAccount(initialBalance) {
  let balance = initialBalance; // 外部无法直接访问

  return {
    deposit(amount) {
      balance += amount;
    },
    withdraw(amount) {
      if (amount <= balance) balance -= amount;
      else console.log('余额不足');
    },
    getBalance() {
      return balance;
    }
  };
}

const account = createAccount(1000);
account.deposit(500);
console.log(account.getBalance()); // 1500
console.log(account.balance); // undefined,无法直接访问

2. 函数工厂(批量创建相似函数)

javascript 复制代码
function multiplier(factor) {
  return (number) => number * factor;
}

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

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

3. 防抖与节流

javascript 复制代码
// 防抖:用户停止输入 300ms 后再执行
function debounce(fn, delay) {
  let timer;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

const handleInput = debounce((e) => {
  console.log('搜索:', e.target.value);
}, 300);

document.querySelector('#search').addEventListener('input', handleInput);

4. 模块模式(封装功能)

javascript 复制代码
const TodoList = (() => {
  const todos = [];
  
  return {
    add(item) { todos.push(item); },
    remove(item) {
      const index = todos.indexOf(item);
      if (index > -1) todos.splice(index, 1);
    },
    getAll() { return [...todos]; }
  };
})();

TodoList.add('学习闭包');
TodoList.add('写一篇博客');
console.log(TodoList.getAll()); // ['学习闭包', '写一篇博客']

经典陷阱:循环中的闭包

错误写法(for + var)

javascript 复制代码
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// 输出:3 3 3(不是期望的 0 1 2)

原因:var 声明的变量共享同一个 i,循环结束后 i 为 3。

正确写法 1:使用 let(推荐)

javascript 复制代码
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// 输出:0 1 2

正确写法 2:使用立即执行函数(IIFE)

javascript 复制代码
for (var i = 0; i < 3; i++) {
  ((j) => {
    setTimeout(() => console.log(j), 100);
  })(i);
}
// 输出:0 1 2

注意:闭包与内存

闭包会持有外部作用域变量的引用,只要闭包存在,这些变量就不会被垃圾回收。如果大量创建闭包且不及时释放,可能导致内存泄漏。

合理使用,用完后将引用置为 null 即可:

javascript 复制代码
let fn = makeCounter();
// 使用...
fn = null; // 释放引用,GC 可以回收了

总结

闭包是 JavaScript 中"函数是一等公民"理念的完美体现。理解了闭包,你就掌握了 JS 中许多高级模式的底层原理------从 React Hooks 到 Promises,背后都有闭包的影子。

你有什么关于闭包的疑问或有趣案例?欢迎在评论区交流!

相关推荐
代码熊崽的编程森林5 小时前
vue + onlyoffice 自定义插件的实现(OnlyOffice 插件:AI 智能编辑)。
前端·javascript·vue.js
Lucky_Turtle5 小时前
【Vue】element plus Slider小数组件设置顺滑程度
前端·javascript·vue.js
Dxy12393102166 小时前
js中Math.min.apply()详解
开发语言·javascript
砍材农夫6 小时前
物联网 基于netty控制报文结构(发布与接收)
java·开发语言·前端·javascript·物联网
上单带刀不带妹6 小时前
Vue3 中 getCurrentInstance() 与 proxy 详解
前端·javascript·vue.js
妄念鹿7 小时前
记一次Uniapp的input输入框type为number时还能输入非数字
前端·javascript
武当王丶也7 小时前
React Native App 内更新实践:从版本策略到 APK 下载和安装
android·javascript·react native
ZTStory7 小时前
Volta 新一代 node 版本管理工具
前端·javascript·node.js
用户938515635077 小时前
数组去重,从双重循环到一行 Set,我经历了什么?
javascript·算法