从0开始学习JavaScript--JavaScript 闭包的应用

在这里插入图片描述

JavaScript的高级概念中,闭包(closure)常常是一个让人感到困惑但又强大的概念。在这篇文章中,将深入探讨闭包的概念以及它在JavaScript中的各种应用场景。

什么是闭包?

在JavaScript中,闭包是指一个函数能够访问并记住其词法作用域,即使该函数在其词法作用域之外执行。这意味着函数可以“捕获”并记住它被创建时的上下文,包括局部变量、参数等。

基本概念

让我们通过一个简单的例子来理解闭包:

function outerFunction() {
  let outerVariable = 'I am from the outer function';

  function innerFunction() {
    console.log(outerVariable);
  }

  return innerFunction;
}

const closureExample = outerFunction();
closureExample(); // 输出: I am from the outer function

在这个例子中,outerFunction 返回了 innerFunction,并且 innerFunction 能够访问 outerVariable,即使 outerFunction 已经执行完毕。这就是闭包的基本概念。

闭包的应用

1. 封装私有变量

闭包允许我们创建私有变量,这对于封装代码非常有用。考虑以下例子:

function counter() {
  let count = 0;

  return {
    increment: function() {
      count++;
    },
    decrement: function() {
      count--;
    },
    getCount: function() {
      return count;
    }
  };
}

const myCounter = counter();
myCounter.increment();
myCounter.increment();
console.log(myCounter.getCount()); // 输出: 2

在这里,count 是一个私有变量,只能通过返回的对象中的方法进行访问和修改。

2. 在回调函数中使用闭包

闭包经常在异步编程中发挥重要作用。考虑以下使用闭包处理回调的情况:

function fetchData(url, callback) {
  fetch(url)
    .then(response => response.json())
    .then(data => callback(null, data))
    .catch(error => callback(error, null));
}

const processResult = (function() {
  let totalRequests = 0;

  return function(error, data) {
    if (error) {
      console.error('Error fetching data:', error);
    } else {
      totalRequests++;
      console.log('Data:', data);
      console.log('Total Requests:', totalRequests);
    }
  };
})();

fetchData('https://api.example.com/data1', processResult);
fetchData('https://api.example.com/data2', processResult);

在这个例子中,processResult 是一个闭包,它能够访问并修改外部函数的 totalRequests 变量,用于跟踪总共发起了多少次请求。

3. 创建函数工厂

闭包还可以用于创建函数工厂,动态生成函数。以下是一个简单的例子:

function greetingGenerator(greeting) {
  return function(name) {
    console.log(`${greeting}, ${name}!`);
  };
}

const sayHello = greetingGenerator('Hello');
const sayHi = greetingGenerator('Hi');

sayHello('Alice'); // 输出: Hello, Alice!
sayHi('Bob');      // 输出: Hi, Bob!

在这里,greetingGenerator 是一个函数工厂,它返回一个新的函数。这个新函数是一个闭包,它能够访问外部函数中的 greeting 变量。

闭包的注意事项

在使用闭包时,有一些注意事项需要考虑,以避免潜在的问题。

1. 内存泄漏

由于闭包可以访问外部函数的变量,如果闭包被长时间引用,可能导致内存泄漏。确保在不再需要时解除对闭包的引用,可以通过解除事件监听器、清除定时器等方式来避免内存泄漏。

function setupEventListener() {
  let count = 0;

  const button = document.getElementById('myButton');
  button.addEventListener('click', function handleClick() {
    count++;
    console.log(`Button clicked ${count} times.`);
  });

  // 错误的方式(可能导致内存泄漏)
  // button.removeEventListener('click', handleClick);
}

在上面的例子中,如果 handleClick 不在需要时没有被正确地移除事件监听器,就可能导致内存泄漏。

2. 共享闭包中的变量

在某些情况下,多个闭包可能共享相同的外部变量。这可能导致一些意外的行为,特别是在涉及异步操作时。为了避免这种情况,通常会使用函数工厂来创建独立的闭包。

function createCounter() {
  let count = 0;

  return {
    increment: function() {
      count++;
    },
    getCount: function() {
      return count;
    }
  };
}

const counter1 = createCounter();
const counter2 = createCounter();

counter1.increment();
console.log(counter1.getCount()); // 输出: 1
console.log(counter2.getCount()); // 输出: 0

在这个例子中,counter1counter2 是独立的闭包,它们各自有自己的 count 变量。

高阶用法:柯里化(Currying)

柯里化是一种通过将多个参数的函数转换为一系列使用一个参数的函数的技术。闭包在实现柯里化时发挥了重要作用。以下是一个简单的柯里化示例:

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn(...args);
    } else {
      return function(...moreArgs) {
        return curried(...args, ...moreArgs);
      };
    }
  };
}

function add(a, b, c) {
  return a + b + c;
}

const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 输出: 6
console.log(curriedAdd(1, 2)(3)); // 输出: 6
console.log(curriedAdd(1)(2, 3)); // 输出: 6

在这个例子中,curry 函数接受一个函数 fn,并返回一个新的函数,该新函数可以通过多次调用实现柯里化。这是通过闭包记住每一次调用的参数,然后根据参数数量决定是执行 fn 还是返回一个新的函数。

总结

通过这篇文章,深入了解了JavaScript闭包的概念及其在实际编程中的应用。闭包不仅能够帮助大家更好地封装代码,而且在处理回调函数和创建函数工厂等方面都能发挥重要作用。深入理解和熟练运用闭包将有助于写出更加灵活、模块化的JavaScript代码。希望这些例子能够帮助你更好地理解闭包的实际应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/199878.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LINUX入门篇【11】---进程篇【3】---进程优先级,进程切换,进程调度

前言: 有了前面知识点的铺垫,本篇我们将围绕进程的三个方面来展开,即进程优先级,进程切换以及进程调度的问题,这里的进程调度其实本质就是CPU是如何去调度进程的。 进程优先级: 优先级的概念&#xff1a…

matlab配置

matlab配置 windowslinux挂载安装MATLAB windows 按照这里一步步配置就行( 移动硬盘中软件备份中自取) linux linux配置步骤 挂载 sudo mount -t auto -o loop /media/oyk/Elements/ubuntu/MATLAB/R2017a_glnxa64_dvd1.iso ./matlab/安装MATLAB 挂载完成后,先…

JS之Object.defineProperty方法

给对象添加属性的方法有许多,这次让我为大家介绍一种给对象添加属性的静态方法吧! 语法:Objcet.defineProperty(对象的名称,“添加的键名”,{value:键值}) const obj {name:"张三",age:18}// 我…

MySQL索引使用总结

索引(index) 官方定义:一种提高MySQL查询效率的数据结构 优点:加快查询速度 缺点: 1.维护索引需要消耗数据库资源 2.索引需要占用磁盘空间 3.增删改的时候会影响性能 索引分类 索引和数据库表的存储引擎有关,不同的存储引擎&am…

广州华锐视点:基于VR元宇宙技术开展法律法规常识在线教学,打破地域和时间限制

随着科技的飞速发展,人类社会正逐渐迈向一个全新的时代——元宇宙。元宇宙是一个虚拟的、数字化的世界,它将现实世界与数字世界紧密相连,为人们提供了一个全新的交流、学习和娱乐平台。在这个充满无限可能的元宇宙中,法律知识同样…

docker镜像原理

什么是镜像 容器解决应用开发、测试和部署的问题,而镜像解决应用部署环境问题。镜像是一个只读的容器模板, 打包了应用程序和应用程序所依赖的文件系统以及启动容器的配置文件,是启动容器的基础。镜像所打 包的文件内容就是容器的系统运行环…

迷你洗衣机哪个牌子好又实惠?口碑最好的小型洗衣机

不得不说洗衣机的发明解放了我们的双手,而我们从小到大就有这个意识,贴身衣物不可以和普通的衣服一起丢进去洗衣机一起,而内衣裤上不仅有肉眼看见的污渍还有手上根本无法消灭的细菌,但是有一款专门可以将衣物上的细菌杀除的内衣洗…

界限与不动产测绘乙级申请条件

整理一期关于测绘资质界限与不动产测绘乙级资质的申请要求 测绘资质是由测绘资质主管部门自然资源部制定的 想要了解标准、正规的申请条件,可以到当地省份的政务网搜索测绘资质办理相关标准(例如下图) 1、通用标准 http://gi.mnr.gov.cn/20…

vue3中的setup()函数详解

​🌈个人主页:前端青山 🔥系列专栏:Vue篇 🔖人终将被年少不可得之物困其一生 依旧青山,本期给大家带来vue篇专栏内容:vue3-setup()函数 目录 setup()函数 1.1 基本使用 1.2 访问 Prop 1.3 Setup的上下文 1.4 与渲…

【古月居《ros入门21讲》学习笔记】16_tf坐标系广播与监听的编程实现

目录 说明: 1. 实现过程(C) 创建功能包(C) 创建tf广播器代码(C) 创建tf监听器代码(C) 配置tf监听器与广播器代码编译规则 编译并运行 编译 运行 2. 实现过程&a…

CCFCSP试题编号:202109-2试题名称:非零段划分

用差分法 #include<iostream> #include<algorithm> #include<cstring> using namespace std;const int N 500000; const int M 10000; int a[N 2 ] { 0 }; int d[M 1] { 0 };int main() {int n;cin >> n;for (int i 1; i < n; i){cin >&g…

泛微E-Office SQL注入漏洞复现

0x01 产品简介 泛微E-Office是一款标准化的协同 OA 办公软件&#xff0c;泛微协同办公产品系列成员之一,实行通用化产品设计&#xff0c;充分贴合企业管理需求&#xff0c;本着简洁易用、高效智能的原则&#xff0c;为企业快速打造移动化、无纸化、数字化的办公平台。 0x02 漏…

数组filter()方法的使用

输入价格后失去焦点就展示符合条件的商品&#xff0c;没有符合条件的商品就弹框提示 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-…

在ubuntu系统安装SVN服务端,并通过客户端进行远程访问

文章目录 前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件 3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口 5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6…

Python变量及其使用

无论使用什么语言编程&#xff0c;总要处理数据&#xff0c;处理数据就需要使用变量来保存数据。 形象地看&#xff0c;变量就像一个个小容器&#xff0c;用于“盛装”程序中的数据。常量同样也用于“盛装”程序中的数据。常量与变量的区别是&#xff1a;常量一旦保存某个数据…

自写一个函数将js对象转为Ts的Interface接口

如今的前端开发typescript 已经成为一项必不可以少的技能了&#xff0c;但是频繁的定义Interface接口会给我带来许多工作量&#xff0c;我想了想如何来减少这些非必要且费时的工作量呢&#xff0c;于是决定写一个函数&#xff0c;将对象放进它自动帮我们转换成Interface接口&am…

oracle免费资源 终止实例 以及新建一台实例的折腾记录

事情的背景是这样的&#xff0c;我的一台oracle小鸡&#xff0c;不太好用的样子&#xff0c;有时候SSH连不上&#xff0c;有时候莫名其妙卡住。所以我就想把它重新安装一下系统&#xff0c;恢复成最初的样子。 然后在网上查资料&#xff0c;是有办法把系统重装一下的。但是略微…

numpy知识库:np.random.randint()用法及其使用场景举例

randint函数解析 import numpy as np# 【随机】返回[0,5)范围内的一个整数 # [0, 5) --> 左闭右开区间 int_a np.random.randint(5) # int_a 可能为 0、1、2、3、4 int_a np.random.randint(low5) # int_a 可能为 0、1、2、3、4# 【随机】返回[1,5)范围内的一个整数 int_…

分布式事务-两阶段提交2PC

2PC协议就是两阶段提交&#xff0c;用来解决分布式事务&#xff0c;分为两个阶段&#xff0c;分别为Prepare和Commit&#xff0c;也是PC由来。 第一阶段Prepare 提交事务请求 如图所示&#xff0c;主要流程有以下三个方面 询问&#xff1a;事务协调者(Manager)向所有的事务参与…

如何拥有免费的docker镜像仓库

shigen日更文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 hello&#xff0c;伙伴们&#xff0c;最近在研究devops的事情&#xff0c;发现了很有意思的东西。 就是我们所有…
最新文章