当前位置: 首页 > article >正文

JavaScript闭包(Closure)详解与应用实例

JavaScript闭包(Closure)详解与应用实例

闭包(Closure)是 JavaScript 中一个非常重要的概念,它与函数的作用域紧密相关。闭包可以用来创建局部变量,这些变量可以跨函数访问,这在很多编程场景中非常有用。以下是闭包的详细讲解:

1. 作用域链

在 JavaScript 中,每个函数都有自己的作用域。当函数执行时,会创建一个执行上下文(execution context),其中包括一个作用域链。作用域链是一个指针列表,用于在查找变量时确定在哪个作用域中查找。

2. 词法作用域

JavaScript 使用词法作用域,这意味着变量的作用域在代码编写时就确定了,而不是在执行时。函数在哪里被声明将决定它的作用域。

3. 闭包的定义

闭包是一个函数和其周围状态(词法环境)的组合。具体来说,闭包让你可以访问函数内部的变量,即使函数已经执行完毕,这些变量仍然可以被访问。

4. 闭包的创建

闭包通常在以下几种情况下创建:

  • 一个函数内部定义了另一个函数。
  • 一个函数外部的变量在函数内部被使用。
  • 函数作为回调函数被传递。

5. 闭包的使用场景

1. 数据封装

闭包可以用来封装数据和功能,创建私有变量和方法。

function createCounter() {
  let count = 0;
  return function() {
    count += 1;
    console.log(count);
  };
}

const counter = createCounter();
counter(); // 输出 1
counter(); // 输出 2
1.1代码详解

这段代码定义了一个名为 createCounter 的函数,它用于创建一个计数器。这个计数器是一个闭包,它能够记住并更新一个名为 count 的变量的值。下面是这段代码的逐行解释:

  1. function createCounter() {:定义了一个名为 createCounter 的函数。

  2. let count = 0;:在 createCounter 函数内部,声明了一个局部变量 count 并初始化为 0。这个变量是计数器的当前值。

  3. return function() {createCounter 函数返回一个新的匿名函数。这个匿名函数是计数器的核心,它将在每次被调用时增加 count 的值。

  4. count += 1;:在匿名函数内部,每次调用这个函数时,count 的值会增加 1。

  5. console.log(count);:增加 count 后,会通过 console.log 将当前的 count 值输出到控制台。

  6. }:结束匿名函数的定义。

  7. }:结束 createCounter 函数的定义。

  8. const counter = createCounter();:调用 createCounter 函数,并将其返回的匿名函数赋值给变量 counter。此时,counter 变量现在指向这个匿名函数。

  9. counter(); // 输出 1:第一次调用 counter 函数,由于 count 初始值为 0,增加 1 后输出 1。

  10. counter(); // 输出 2:第二次调用 counter 函数,count 的值再次增加 1,输出 2。

这个计数器的关键在于 count 变量被封闭在 createCounter 函数的作用域内,即使 createCounter 函数执行完毕后,count 变量也不会被销毁,因为它被返回的匿名函数引用着。因此,每次调用 counter 函数时,都能正确地访问和更新 count 的值。这就是 JavaScript 中的闭包(closure)概念的一个实际应用。

2. 函数作为参数传递

闭包可以用来将函数作为参数传递给另一个函数。

function repeat(operation, n) {
  let count = 0;
  return function() {
    if (count < n) {
      operation();
      count++;
    }
  };
}

const repeatLog = repeat(console.log, 3);
repeatLog("Hello World"); // 输出 "Hello World" 三次
2.1 代码详解

这段代码定义了一个名为 repeat 的函数,它用于重复执行某个操作指定的次数。下面是这段代码的逐行解释:

  1. function repeat(operation, n) {:定义了一个名为 repeat 的函数,它接受两个参数:operationnoperation 是要重复执行的函数,n 是重复执行的次数。

  2. let count = 0;:在 repeat 函数内部,声明了一个局部变量 count 并初始化为 0。这个变量用于跟踪 operation 被执行的次数。

  3. return function() {repeat 函数返回一个新的匿名函数。这个匿名函数将在每次被调用时检查是否已经执行了 operation 指定的次数。

  4. if (count < n) {:在匿名函数内部,使用一个 if 语句来检查 count 的值是否小于 n。如果是,那么执行下面的代码块。

  5. operation();:如果条件满足,调用传入的 operation 函数。在这个例子中,operationconsole.log 函数。

  6. count++;:执行完 operation 后,增加 count 的值。

  7. }:结束 if 语句。

  8. }:结束匿名函数的定义。

  9. }:结束 repeat 函数的定义。

  10. const repeatLog = repeat(console.log, 3);:调用 repeat 函数,并传入 console.log 作为 operation 参数和数字 3 作为 n 参数。这将创建一个新的函数,该函数将 console.log 重复执行 3 次。

  11. repeatLog("Hello World"); // 输出 "Hello World" 三次:调用 repeatLog 函数,并传入字符串 "Hello World" 作为参数。由于 repeatLog 函数是 repeat 函数返回的匿名函数,它将检查 count 的值,并在小于 3 的情况下执行 console.log("Hello World")。由于 count 初始值为 0,所以 console.log("Hello World") 将被执行 3 次,每次调用 repeatLog 时都会执行一次。

这个 repeat 函数利用了闭包的概念,count 变量被封闭在 repeat 函数的作用域内,即使 repeat 函数执行完毕后,count 变量也不会被销毁,因为它被返回的匿名函数引用着。因此,每次调用返回的函数时,都能正确地访问和更新 count 的值。

3. 函数作为返回值

闭包可以作为函数的返回值。

function makeSizer(size) {
  return function(x) {
    return x * size;
  };
}

const double = makeSizer(2);
console.log(double(5)); // 输出 10
3.1 代码详情

这段代码定义了一个名为 makeSizer 的函数,它用于创建一个可以根据给定大小进行缩放的函数。下面是这段代码的逐行解释:

  1. function makeSizer(size) {:定义了一个名为 makeSizer 的函数,它接受一个参数 size,这个参数表示缩放的比例。

  2. return function(x) {makeSizer 函数返回一个新的匿名函数。这个匿名函数接受一个参数 x,表示要进行缩放的值。

  3. return x * size;:在匿名函数内部,返回 xsize 的乘积,即 xsize 缩放后的结果。

  4. }:结束匿名函数的定义。

  5. }:结束 makeSizer 函数的定义。

  6. const double = makeSizer(2);:调用 makeSizer 函数,并传入数字 2 作为 size 参数。这将创建一个新的函数,该函数将任何传入的值乘以 2。

  7. console.log(double(5)); // 输出 10:调用 double 函数,并传入数字 5 作为参数。由于 double 函数是 makeSizer 函数返回的匿名函数,它将 5 乘以 2,并返回结果 10。然后,使用 console.log 将这个结果输出到控制台。

这个 makeSizer 函数利用了闭包的概念,size 参数被封闭在 makeSizer 函数的作用域内,即使 makeSizer 函数执行完毕后,size 也不会被销毁,因为它被返回的匿名函数引用着。因此,每次调用返回的函数时,都能正确地访问 size 的值,并使用它来进行计算。在这个例子中,double 函数实际上是一个闭包,它记住了 size 的值为 2,并在每次调用时使用这个值。

6. 闭包的缺点

  • 内存泄漏:由于闭包会保持对外部变量的引用,这可能会导致内存泄漏。
  • 性能问题:创建过多的闭包可能会影响性能。

7. 闭包与模块模式

闭包常与模块模式结合使用,用于创建私有变量和公有方法。

const counter = (function() {
  let count = 0;
  return {
    increment: function() {
      count += 1;
    },
    decrement: function() {
      count -= 1;
    },
    value: function() {
      return count;
    }
  };
})();

counter.increment();
console.log(counter.value()); // 输出 1

总结

闭包是 JavaScript 中一个强大的特性,它允许函数访问和操作外部变量。理解闭包有助于编写更有效和模块化的代码。然而,使用闭包时需要注意内存泄漏和性能问题。


http://www.kler.cn/a/373814.html

相关文章:

  • 【Linux:TCP通信流程】
  • c语言水仙花,超简单讲解
  • 【C/C++】模拟实现strlen
  • ALIGN: Tuning Multi-mode Token-level Prompt Alignment across Modalities
  • 使用UDP实现发送和接收
  • Linux 基础io_ELF_虚拟物理地址_动态库加载
  • 财经领域波澜现,茅台价格动心弦。供需关系新篇章,高端白酒市场寒。经济转型消费变,电商大促供需悬。经济压力需求减,理性消费新风传。
  • 智能视频多语言AI配音/翻译工具
  • Java项目实战II基于微信小程序的马拉松报名系统(开发文档+数据库+源码)
  • 【ArcGISPro】宣布推出适用于 ArcGIS 的 AI 助手
  • 51c深度学习~合集6
  • 【系统设计】探索数据库的世界:轻松掌握基本原理
  • 初识字节码文件--Java
  • 【C#】搭建环境之CSharp+OpenCV
  • 树莓派基础设置--1.更新和升级操作系统
  • 文件中台与安全:集成方案的探索与实践
  • REST APIs与微服务:关键差异
  • dicom基础:乳腺影像方位信息介绍
  • ubuntu交叉编译dbus库给arm平台使用
  • 运用通义灵码有效管理遗留代码:提升代码质量与可维护性
  • chrome商店下载的插件转crx安装包
  • openpnp - 手工修改配置文件(元件高度,size,吸嘴)
  • 应用层知识点总结1
  • Java设计模式之代理模式(二)
  • Python变量类型
  • Kubernetes中的网络模型:Service、Ingress、Pod通信详解