javaScript 进阶之路 --- 《加深理解回调函数》

简介: javaScript 进阶之路 --- 《加深理解回调函数》

image.png

加深理解回调函数


前言: 回想当初第一次看到“回调函数”这个名词的时候,真的快把我难哭了。所有视频教程在讲到某个知识点的时候,大概都会说一句:“啊,这里怎么办呢?这里我们就需要用到一个回调函数...”。

等等,喂,关键是你还没讲什么是回调函数啊!,你让我怎么往下继续听啊...于是留下我一个人看着后面几十分钟的视频,但是脑子里还在纠结这个“回调函数”到底是什么玩意。

注意: 本文的前提知识是你需要了解 JS 的执行机制。我强烈建议你先熟悉前提知识点再往下看,因为我们最终的目的是手写 Promise🎉,但是之前讲解的这些知识点是需要你必须掌握的。

一. 函数


回调函数的基本概念我之前的文章虽然有些过,但是为了引入下文,在这里还是简单再提一嘴。我们先看一下 MDN 的解释。

image.png

什么意思呢?还拿我们上一章节的例子。一个 setTimeout 里,隔了1秒去控制台打印一个 conole.log()

image.png

让我们拆解一下。我们仔细看下面的一行代码,你发现了吗?我只不过是把声明一个箭头函数,变为了定义一个匿名函数。其实这两种写法的所产生的效果是一模一样的。(请暂时不要考虑箭头函数和普通函数的区别)

image.png

在这种情况下,上面的匿名函数箭头函数统称为函数 setTimeout 的回调函数。你一定需要明白的事情就是 setTimemout 也是一个函数! 单独的函数是无法被叫做回调函数的,回调函数的概念是相互的。所以更确切的说法应该是:

首先这里的箭头函数和匿名函数作为 setTimout 的参数,然后在 setTimout 内部被调用,最后箭头函数和匿名函数才被称为 setTimout 的回调函数。

我发现其实有很多新手是没有搞懂函数不同的声明方式,所以才导致你们迷惑的。我们这里先来讲一下函数常用的三种定义方式。

image.png

在这里虽然这三个函数的执行结果是一样的,但是它们的区别是不小的。使用 function 关键字声明一个函数。被叫做函数声明。 而下面的两种方法是被叫做函数表达式

image.png

虽然在使用方法上我们都是通过 "()" 去调用,但是其实它们还是有差别的。其中最重要的一个差别就是,使用函数声明定义的一个函数,该变量会被 js 解析器提升到代码开始执行之前。而使用函数表达式定义的函数则不会被提升。什么意思呢?

image.png

你会发现,我们竟然可以在定义 hello 函数之前去使用这个函数。而下面两个函数都报出了在赋值之前调用的错误。

image.png

在这里我们再延伸一个知识点。我们在定义 hello 函数之前,首先定义一个 变量 name

image.png

虽然这里看起来 name 是在 hello 函数之前声明的,但是 hello 这个变量的声明其实是在变量 name 之前的。

其原因用简单的描述就是:js 解析器会在到你这个.js 文件后,会优先去查找使用 function 声明的函数名称,然后把这些函数名保存在一个对象中,并且提前创建一个引用来-->指向这个函数体。 具体细节还需读者自主查询,本文重点不在这里。

如果你读懂了上面的几个环节,那么我觉得这种写法你同样应该明白了。

image.png

那么我们趁热打铁,开启下一篇章。

二. 数据处理方式


我们都知道函数是为了帮助我们完成一些复杂的逻辑运算,可以反复调用的,并且函数是可以传递参数的。

image.png

比如上面的函数,允许我们传递两个数字类型的变量,然后该函数会把这两个数字的和返回给我们。

ok,上面的内容很简单,我想大家应该都明白。如果我们日常和后端打交道的数据传输没有延迟,那么自然而然我们对数据处理起来就会非常方便。什么意思呢?假设我们 向后端发送请求获得数据 ,这一过程是一瞬间完成的,那么我们就可以顺着我们的想法从上往下写非常顺畅。

image.png

如上面刚写的的函数一样,你可以理解为我们向后端服务器请求了一个数据,数据一瞬间就返回了,那么们就可以把这个 data 给返回出去。然后在下面用一个变量去接受一下,随之进行一些运算。

这里我们来简单模拟一个场景。

image.png

我们定义了一个普通对象 data 来模拟后端即将传递给我们的数据。我们假设这个数据的可能需要花费 1s 的时间才能返回。我们可能自然而然的想到,那不是和之前一样嘛,在 setTimeout 里返回不就行了吗。

image.png

你可以先思考一下上面的这段代码,userData 会拿到正确的 data 值吗?

非常遗憾的是,我们是无法在同步的代码里第一时间拿到异步函数的返回值的。

image.png

那么造成上面的代码结果的原因是为什么?为什么是 undefined 呢?我们一步一步分析。

三. 理清思路


当下面画黄线的代码被执行的时候。紧接着下一步我们就需要马上跳进 getData 的函数体中去分析。

image.png

进到这个函数里,首先就是执行 setTimeout 函数。 (我在这里再再再亿次强调,setTimeout 本身就是一个普通函数,没什么特别的。)

image.png

但是 setTimeout 函数会把接收的回调函数给推送到任务队列里去排队。什么?你没看见回调函数?这不就是文章最开头说的函数的声明方式之一箭头函数吗?怎么这么快就忘了?自觉翻回上面去再看一遍⏰。

image.png

紧接着 getData函数 执行完毕,看一眼,函数本身没有返回值,那么默认返回值为 undefined

这里需要特别注意! 我们如果在这里写了 return语句 ,这样才能算是 getData函数的 返回值。

image.png

而在我们的 data 却是 setTimout回调函数的返回值,你能拿到才怪。

那怎么办呢?我们继续往下看。

四. 回调函数


别着急往下看,经过上面的三个环节,我们先做个小测试🎁。

image.png

上面这段代码你看懂了吗?(下面是执行结果)

735f16dae4c7453f89639a819f8b482e_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.jpg

如果你对执行结果不是特别清楚,没关系。到这一步我只希望你能读懂下面这行代码,至少对于写法上,很清楚的知道为什么可以这样写

image.png

如果你暂时还没搞懂,那么我强烈建议你至少回过头再品味一下第一个标题 函数,这次要细读。

如果你读懂了,恭喜你🎉,你距离理解这个名词更近了一步。接下来我们稍微改造一下代码。

image.png

  1. 上面这段代码乍一看很玄乎,别着急,我们慢慢分析。首先 modifyData函数 是我们提前设定好的一个处理后端数据的函数。(前提是如果我们能拿到后端给我们的数据)
  2. 既然我们没有办法在同步函数里拿到 data 后再去处理,那么我们就干脆提前给后端代码函数 getData 一个参数。

一个我们提前设定好的处理函数。当 setTimeout 的回调函数拿到数据后,就帮我们执行这个函数,也就是 modifyData 函数。然后把 data 作为参数传递给 modifyData函数

  1. 我们在 setTimout 的回调函数中打印一下修改前后的值。

image.png

1992212ce5f248dfa877ad73640860f3_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.jpg

我们会发现,我们之后的代码都在回调函数里去处理的话,就能完美实现需求。也就是后端传过来的数据我们可以拿到了,并且可以处理成我们想要的数据了。

结语


在这里我们就先不引入解回调地狱的概念了了,我害怕都的读者确实会绕迷糊。 我会在放在手写 Promise 之前,引入这个概念。 希望读者可以细细体会上面的代码,一旦学会这些概念,你会看到和之前不一样的 js 世界🎁。



相关文章
|
1月前
|
XML 前端开发 JavaScript
JavaScript进阶 - AJAX请求与Fetch API
【7月更文挑战第3天】前端开发中的异步基石:AJAX与Fetch。AJAX,使用XMLHttpRequest,处理跨域、回调地狱和错误处理。Fetch,基于Promise,简化请求,但需注意默认无跨域头和HTTP错误处理。两者各有优劣,理解其问题与解决策略,能提升前端应用的性能和用户体验。
|
1月前
|
前端开发 JavaScript 安全
JavaScript进阶-JavaScript库与框架简介
【7月更文挑战第11天】JavaScript库和框架加速Web开发,但也带来挑战。选择适合项目、团队技能的库或框架,如React、Angular、Vue,是关键。保持依赖更新,注意性能优化,避免过度依赖。遵循最佳实践,确保安全性,如防XSS和CSRF。学习基础,结合代码示例(如React计数器组件),提升开发效率和应用质量。
|
1月前
|
资源调度 JavaScript 前端开发
JavaScript进阶 - JavaScript库与框架简介
【7月更文挑战第5天】JavaScript库和框架构成了前端开发的核心,如jQuery简化DOM操作,Angular、React和Vue提供全面解决方案。选择时要明确需求,避免过度工程化和陡峭学习曲线。使用版本管理工具确保兼容性,持续学习以适应技术变化。示例展示了jQuery和React的简单应用。正确选择和使用这些工具,能提升开发效率并创造优秀Web应用。
|
1月前
|
缓存 JavaScript 前端开发
JavaScript进阶 - Web Workers与Service Worker
【7月更文挑战第4天】JavaScript的Web Workers和Service Worker增强了Web性能。Web Workers处理后台多线程,减轻主线程负担,但通信有开销,受同源策略限制。Service Worker则用于离线缓存和推送通知,需管理其生命周期、更新策略,并确保安全。两者都带来了挑战,但也极大提升了用户体验。通过理解和优化,开发者能构建更高效、安全的Web应用。
|
26天前
|
JavaScript 前端开发 API
Node中的AsyncLocalStorage 使用问题之Node.js将 JavaScript 层的 nativeHooks 注册到 C++ 层的问题如何解决
Node中的AsyncLocalStorage 使用问题之Node.js将 JavaScript 层的 nativeHooks 注册到 C++ 层的问题如何解决
|
1月前
|
缓存 前端开发 JavaScript
JavaScript进阶 - Web Workers与Service Worker
【7月更文挑战第10天】在Web开发中,Web Workers和Service Worker提升性能。Workers运行后台任务,防止界面冻结。Web Workers处理计算密集型任务,Service Worker则缓存资源实现离线支持。常见问题包括通信故障、资源限制、注册错误及缓存更新。通过示例代码展示了两者用法,并强调生命周期管理和错误处理的重要性。善用这些技术,可构建高性能的Web应用。
|
1月前
|
XML 前端开发 JavaScript
JavaScript进阶 - AJAX请求与Fetch API
【7月更文挑战第9天】JavaScript进阶:AJAX与Fetch API对比。AJAX用于异步数据交换,XMLHttpRequest API复杂,依赖回调。Fetch API是现代、基于Promise的解决方案,简化请求处理。示例:`fetch('url').then(r => r.json()).then(data => console.log(data)).catch(err => console.error(err))`。注意点包括检查HTTP状态、错误处理、CORS、Cookie和超时。Fetch提高了异步代码的可读性,但需留意潜在问题。
|
1月前
|
存储 JavaScript 前端开发
JavaScript进阶 - 浏览器存储:localStorage, sessionStorage, cookies
【7月更文挑战第8天】Web开发中的客户端存储技术,如`localStorage`, `sessionStorage`和`cookies`,用于保存用户设置和跟踪活动。`localStorage`持久化存储,`sessionStorage`随页面会话消失。两者提供基本的增删查改操作,但有大小限制和安全风险。`cookies`适合会话管理,可设置过期时间并能跨域。使用时注意存储量、安全性和跨域策略,选择适合场景的存储方式。
|
1月前
|
设计模式 JavaScript 前端开发
JavaScript进阶 - JavaScript设计模式
【7月更文挑战第7天】在软件工程中,设计模式是解决常见问题的标准解决方案。JavaScript中的工厂模式用于对象创建,但过度使用可能导致抽象过度和缺乏灵活性。单例模式确保唯一实例,但应注意避免全局状态和过度使用。观察者模式实现了一对多依赖,需警惕性能影响和循环依赖。通过理解模式的优缺点,能提升代码质量。例如,工厂模式通过`createShape`函数动态创建对象;单例模式用闭包保证唯一实例;观察者模式让主题对象通知多个观察者。设计模式的恰当运用能增强代码可维护性。
|
1月前
|
缓存 JavaScript 前端开发
JavaScript进阶 - Web Workers与Service Worker
【7月更文挑战第6天】JavaScript的Web Workers和Service Worker增强了浏览器的性能处理和离线功能。Web Workers处理后台计算,减轻主线程压力,但通信有开销,受同源策略限制。Service Worker则能拦截网络请求,支持离线缓存和推送通知,但其生命周期和权限管理需谨慎处理。通过理解它们的工作原理和限制,开发者能创建更流畅、更健壮的Web应用。

热门文章

最新文章