千家信息网

JS中为什么有闭包

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,本篇内容主要讲解"JS中为什么有闭包",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"JS中为什么有闭包"吧!太长不看版当「函数访问在其外部定义的变量时」,你
千家信息网最后更新 2026年11月02日JS中为什么有闭包

本篇内容主要讲解"JS中为什么有闭包",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"JS中为什么有闭包"吧!

太长不看版

当「函数访问在其外部定义的变量时」,你需要闭包。

例如,这段代码包含一个闭包:

let users = ['Alice', 'Dan', 'Jessica']; let query = 'A'; let user = users.filter(user => user.startsWith(query));

注意 user => user.startsWith(query) 本身是一个函数。它使用了 query 变量。但是,query 变量是在该函数的"外部"定义的。那就是闭包。

「如果你愿意,可以在这里就停止阅读」。本文的其余部分会以不同的方式去处理闭包,并不解释闭包是什么,而是带你完成「发现」闭包的过程——就像1960年代的第一批程序员所做的那样。

第1步:函数可以访问外部变量

要了解闭包,我们需要对变量和函数有所了解。在这个例子中,我们在 eat 函数中声明了 food 变量。

function eat() {   let food = 'cheese';   console.log(food + ' is good'); }  eat(); // => 'cheese is good'

但是,如果我们以后想更改 eat 函数的 food 变量,该怎么办?为此,我们可以将 food 变量本身从函数中移到顶层:

let food = 'cheese'; // 我们把它移动到外部  function eat() {   console.log(food + ' is good'); }

这样我们可以在任何有需要的时候"从外部" 修改 food:

eat(); // => 'cheese is good' food = 'pizza'; eat(); // => 'pizza is good' food = 'sushi'; eat(); // => 'sushi is good'

换句话说,food 变量不再是 eat 函数的局部变量,但是 eat 函数仍然可以轻松访问它。「函数可以访问它们之外的变量」。先停下来想一秒钟,确保你对这个想法没有任何疑问。然后继续执行第二步。

第2步:在函数调用中包装代码

假设我们有一些代码:

/* 一些代码片段 */

这些代码做什么无关紧要。但是,假设「我们要运行两次」。

一种方法是复制并粘贴:

/* 一些代码片段 */ /* 一些代码片段 */

另一种方法是使用循环:

for (let i = 0; i < 2; i++) {   /* 一些代码片段 */ }

第三种方法,也是我们今天特别感兴趣的一种方法,将其包装在一个函数中:

function doTheThing() {   /* 一些代码片段 */ }  doTheThing(); doTheThing();

函数为我们提供了很大的灵活性,因为我们可以随时在程序中的任何位置把这个函数执行任意次。

如果愿意,「我们也可以只调用一次」:

function doTheThing() {   /* 一些代码片段 */ }  doTheThing();

请注意,上面的代码与原始代码段是等效的:

/* 一些代码片段 */

换句话说,「如果我们有一段代码,将代码"包装"到一个函数中,然后只调用一次,那么我们就不会改变代码的作用」。我们会忽略此规则的一些例外,但总的来说这应该是有道理的。停留在这个想法上,直到你的大脑完全理解为止。

第3步:发现闭包

前面我们通过两种不同的想法进行了探索:

  • 函数可以访问在其外部定义的变量。

  • 在函数中包装代码并调用一次不会改变结果。

那么如果把它们结合在一起会发生些什么呢。

我们将从第一步的代码开始:

let food = 'cheese';  function eat() {   console.log(food + ' is good'); }  eat();

然后,将整个例子中的代码包装到一个函数中,该函数将被调用一次:

function liveADay() {   let food = 'cheese';    function eat() {     console.log(food + ' is good');   }    eat(); }  liveADay();

再次审视两个代码片段,并确保它们是等效的。

这段代码有效!但是仔细看,注意 eat 函数在 liveADay 函数的内部。这允许吗?我们真的可以将一个函数放在另一个函数中吗?

在某些语言中,用这种方式写出来的代码是「无效」的。例如这种代码在 C 语言(没有闭包)中无效。这意味着在 C 语言中,前面的第二个结论是不正确的——我们不能随随便便就把一些代码包装在函数中。但是 JavaScript 不受这种限制。

再看这段代码,并注意在哪里声明和使用了 food:

function liveADay() {   let food = 'cheese'; // 声明 `food`    function eat() {     console.log(food + ' is good'); // 使用 `food`   }    eat(); }  liveADay();

让我们一起逐步看一下这段代码。首先在顶层声明 liveADay 函数,然后立即调用它。它有一个 food 局部变量,还包含一个 eat 函数。然后调用 eat 功能。因为 eat在 liveADay 内部,所以它"看到"了所有变量。这就是为什么它可以读取 food 变量的原因。

「这就是闭包」。

「我们说当函数(例如 eat)读取或写入在其外部(例如在 food 中)声明的变量(例如 food)时,存在闭包。」

花一些时间多读几遍,并确保你已经理解了上面的代码代码。

下面是本文最开始介绍过的例子:

let users = ['Alice', 'Dan', 'Jessica']; let query = 'A'; let user = users.filter(user => user.startsWith(query));

如果用函数表达式重写,则更容易注意到闭包:

let users = ['Alice', 'Dan', 'Jessica']; // 1. 查询变量在外部声明 let query = 'A'; let user = users.filter(function(user) {   // 2. 我们处于嵌套函数中   // 3. 然后我们读取查询变量(在外部声明!)   return user.startsWith(query); });

每当函数访问在其外部声明的变量时,我们就说它是一个闭包。这个术语本身在使用时有些宽松。在本例中,有些人把「嵌套函数本身」称为"闭包"。其他人可能会把访问外部变量的"技术"称为闭包。实际上这都没关系。

函数调用的幽灵

闭看似简单,但是这并不意味着他们没有自己的陷阱。如果你真正考虑一下,函数可以在外部读取和写入变量的事实将会产生深远的影响。这意味着只要可以调用嵌套函数,这些变量就会"存活"下去:

function liveADay() {   let food = 'cheese';    function eat() {     console.log(food + ' is good');   }    // Call eat after five seconds   setTimeout(eat, 5000); }  liveADay();

在这里,food 是在 liveADay() 函数调用内的局部变量。在我们退出 liveADay 之后,很容易想到它"消失了",并且它不会回来困扰我们。

但是,在 liveADay 内部,我们告诉浏览器在五秒钟内调用 eat。然后,eat 读取 food 变量。「因此,JavaScript引擎需要使特定的 liveADay() 调用中的food变量保持可用,直到调用eat。」

从这种意义上讲,我们可以将闭包视为过去函数调用的"幻象"或"内存"。即使我们的 liveADay() 函数调用已经完成很长时间,但只要仍可以调用嵌套的 eat 函数,那么它的变量就必须继续存在。幸运的是,JavaScript 为我们做到了这一点,因此我们就无需再去考虑它了。

为什么会有"闭包"?

最后,你可能想知道为什么以这种方式调用闭包。主要是历史原因。一位熟悉计算机科学术语的人可能会说像 user => user.startsWith(query) 之类的表达式具有"开放绑定"。换句话说,从中可以清楚地知道 user 是什么(一个参数),但是还不能确定 query 是孤立的。当我们说"实际上,query 指的是在外部声明的变量"时,我们是在"关闭"开放绑定。换句话说,我们得到一个 闭包。

并非所有语言都实现闭包。例如在一些像 C 这样的语言中,根本不允许嵌套函数。结果,一个函数只能访问自己的局部变量或全局变量,永远不会出现访问父函数的局部变量的情况。当然,这种限制是痛苦的。

还有像 Rust 这样的语言,它们实现了闭包,但是对于闭包和常规函数有着单独的语法。因此,如果你想从函数外部读取变量,则必须在 Rust 中选择使用该变量。这是因为在底层,即使在函数调用之后,闭包也可能要求引擎保持外部变量(称为"环境")。这种开销在 JavaScript 中是可以接受的,但是对于非常低级的语言来说,则可能会引发性能方面的问题。

到此,相信大家对"JS中为什么有闭包"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

函数 变量 代码 闭包 片段 语言 局部 方法 换句话说 是在 包装 例子 实际 就是 想法 意味 方式 查询 不同 兴趣 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 喔噻互联网科技有限公司 互联网科技公司简介 互联网科技工作委员会副主任毛军 互联网科技开发公司 唐信互联网科技 上海致骋互联网科技 融势互联网科技 新华互联网科技校企合作 银川新华互联网科技职业技术培训学校图片 互联网软件和科技 宝灵互联网科技有限公司 杭州壁岐互联网科技有限公司 美国互联网科技实力 梅州GM互联网络科技有限公司 广州千川互联网科技有限公司 富国互联网金融科技混合基金 35岁以上互联网科技行业 东营紫格互联网科技 湖北辰鑫科技互联网 银川新华互联网科技学校原画 西安纸贵互联网科技有限公司 广州百单互联网科技有限公司 互联网最新的科技 互联网巨头反垄断金融科技 深圳艾佳互联网科技 长亮科技 互联网金融 互联网科技中介 山东子淼互联网科技有限公司 艺电能源互联网科技有限公司招聘 唐山双旺合互联网科技产业园
0