闭包:JavaScript里的"时空背包客"

78 阅读4分钟

前言:很多同学对于闭包应该听过很多这样的说法:什么避免变量全局污染;使数据私有化,外部无法修改内部数据;可以让外部可以使用内部的私有数据。以上准确的来说应该算是函数的作用,而且闭包也不止一个函数,那函数有的特性闭包肯定有,单独拿出来说还是比较容易混淆的。

闭包的核心作用就是:使变量可以驻留在内存,不被回收。

一、闭包是什么?——一个背着零食旅行的函数

ps:简易理解:函数里面嵌套函数,且内部函数携带外层函数给的原始作用域(变量,函数,模块等)在其他作用域浪,就会形成闭包。(好像小朋友带着家里钱被坏叔叔骗家里去还被关小黑屋了)

想象你有个爱囤零食的朋友,每次出门都要在背包里塞满饼干。JavaScript的函数就像这位朋友,而**闭包(Closure)**就是它随身携带的"零食背包"。官方说法是:函数和其词法环境的引用捆绑在一起

举个栗子 :

function 旅行准备() {
  const 零食 = "曲奇饼";
  return function() {
    console.log("我偷偷带了:" + 零食);
  };
}

const 间谍函数 = 旅行准备();
间谍函数(); // 输出:我偷偷带了:曲奇饼

这个贪吃鬼函数明明已经执行完毕,却还能在背包(闭包)里保留着零食变量,是不是很像哆啦A梦的四次元口袋?

人话:function函数在该定义的词法作用域之外被执行——盆右,闭包来咯!(只要使用了回调函数就是使用了闭包)

讲道理,旅行准备这个函数在被执行后其整个内部作用域都会被销毁(诶!闭包不让),让我们看看下面的例子。


二、闭包实战手册——那些年我们踩过的坑

案例1:会自己长大的计数器

function 创造计数器() {
  let count = 0;
  return {
    增值: () => ++count,
    查账: () => count
  };
}

const 我的小金库 = 创造计数器();
我的小金库.增值(); // 1
我的小金库.查账(); // 1

这个计数器就像会下金蛋的鹅,每次调用都在自己的秘密空间里默默记账。

创造计数器函数内部作用域被闭包保留了,count就能被引用。

案例2:定时器谜案(经典面试题)

ps:setTimeout是js引擎内置函数,下面意思就是100毫秒后执行这个函数。

for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i); // 输出3个3!
  }, 100);
}

为什么是3个3?这里原理就是当定时器运行时即使每个迭代中执行的时setTimeout(...,0),所有的回调函数依然是在循环结束才会被执行。

这里定时器像三个视力模糊的侦探,全都指向同一个i变量。解决办法是给每个侦探配个相机(闭包):

for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i); // 0,1,2
  }, 100);
}

此处使用了let声明(能劫持块作用域,并在内声明一个变量),上面就是每次迭代都声明。随后的每个迭代都会使用上一个迭代结束时的值来初始化这个变量。

或者用IIFE制造独立取证室:

for (var i = 0; i < 3; i++) {
  (function(j){
    setTimeout(() => {
      console.log(j); // 0,1,2
    }, 100);
  })(i);
}

在迭代内使用IIFE会为每个迭代都生成一个新的作用域,使得延迟函数的回调可以将新的作用域封闭在每个迭代内部,每个迭代中都会含有一个具有正确值的变量(上面的“j”)供我们访问。


三、闭包生存法则——不要变成内存刺客

  1. 内存泄漏风险:闭包就像粘人的小妖精,会一直抓着变量不放。用完记得:
const 我的小金库 = 创造计数器();
// 使用完毕后...
我的小金库 = null; // 解除引用

上面我们讲到闭包的核心作用:使变量可以驻留在内存,不被回收。这既是优点也是缺点,数据长时间驻留在内存不被回收就会内存泄漏。双刃剑,看着用就行。

  1. 性能优化:避免在循环中创建大量闭包,就像不要在洗衣机里塞满曲奇饼——会糊!

四、总结:闭包是JavaScript的瑞士军刀

  • ✅ 优点:实现封装、缓存数据、创建私有空间
  • ⚠️ 注意:适度使用,避免内存泄漏
  • 🎮 玩法:模块开发、防抖节流、高阶函数

最后送你一句程序员谚语:"不懂闭包的程序员,就像不会系鞋带的跑者——跑不远!" 🏃💨

(本文不承诺曲奇饼不会在闭包中过期,请定期清理内存~)