4 个JavaScript 中一流函数的日常用例

web前端开发

共 5869字,需浏览 12分钟

 ·

2021-12-24 19:27

英文 | https://medium.com/codex/4-daily-use-cases-of-first-class-functions-in-javascript-17b7079a6217

翻译 | 杨小爱


每种语言都有使其与众不同的功能。在这篇文章中,我想提到 JavaScript 中的“一流函数”特性。这篇文章不是关于如何使用它的教程。相反,我想指出该功能的一些实际用途以及它给开发人员带来的好处。
好吧,开始吧!
什么是“一流函数”?
维基百科的定义:
在计算机科学中,如果一种编程语言将函数视为一等公民,那么它就被称为具有一等函数。这意味着该语言支持将函数作为参数传递给其他函数,将它们作为其他函数的值返回,并将它们分配给变量或将它们存储在数据结构中。
例如,在 JavaScript 中,我们可以将一个函数分配给一个变量。
var sum = function(a, b) {  return a + b;}var total = sum(10, 1);

如果我们是第一次阅读,这个定义会有点混乱。然而,事实是我们在不知情的情况下使用了它。

AddEventListener — 学习 JavaScript 的第一课

过去,引入 JavaScript 是为了向网站添加动态行为。例如,我们希望在用户单击按钮时更改文本。当有人学习 JavaScript 时,这是第一行代码。

<html>  <body>    <p id="text">The text will change when you click on the button</p>    <button type="button" id="btn">Click me!</button>    <script>      let btn = document.getElementById("btn");      let text = document.getElementById("text");
btn.addEventListener("click", function() { text.innerHTML = "New text!" });</script> </body></html>

在第 9 行,我们将函数作为参数传递给 addEventListener 方法。该功能与按钮的“单击”事件相关联。当事件被触发时,该函数将运行。

让我们好奇——第 1 部分

要了解该功能的作用,让我们考虑一下它不可用的语言。无论使用何种编程语言,添加动态行为在 UI 开发中都很常见。如果我们不能将函数作为参数传递怎么办?我期待您在留言区与我们分享您的看法。

发送 HTTP 请求——JavaScript 中的常见任务

我以Axios为例。它是最流行的用于发送 HTTP 请求的 JavaScript 库之一。在一个项目中,我们可能需要添加一些常用的配置。

例如,要将 JWT 发送到服务器,我们希望将标头 Authorization 添加到所有请求。因此,我们需要一个函数来抓取 JWT 并将其添加到标题中。

可以使用拦截器来完成。

// Add jwt to all requests using interceptorsaxios.interceptors.request.use(function (config) {
const jwt = globalStore.getJWT(); // assume the token is saved in a global store. config.headers["Authorization"] = `Bearer ${jwt}`; return config;}, null, { synchronous: true });

同样,我们传递 2 个函数作为 use 方法的参数。第一个函数在请求的标头中设置令牌。

如果出现错误,则第二个函数将运行(为简单起见,我们没有在此处定义它)。Axios在处理一个请求时,会一一运行所有的拦截器,将用户的配置转化为完整配置。

然后它将请求发送到服务器。

Axios 处理拦截器的方式很好地说明了 JavaScript 中的一流函数。

// ------------------------------- inside inteceptor's use method ------------------------//InterceptorManager.prototype.use = function use(fulfilled, rejected, options) {  this.handlers.push({    fulfilled: fulfilled,    rejected: rejected,    synchronous: options ? options.synchronous : false,    runWhen: options ? options.runWhen : null  });  return this.handlers.length - 1;};
// -------------------------------- process interceptors ---------------------------------//// filter out skipped interceptorsvar requestInterceptorChain = [];var synchronousRequestInterceptors = true;this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) { return; }
synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;
requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);});
// ------------------------------- execute synchronous interceptors ---------------------//var newConfig = config;while (requestInterceptorChain.length) { var onFulfilled = requestInterceptorChain.shift(); var onRejected = requestInterceptorChain.shift(); try { newConfig = onFulfilled(newConfig); } catch (error) { onRejected(error); break; }}

在第 23 行,我们在 usemethod 中传递的已完成和被拒绝的函数被添加到 requestInterceptorChain。我们将函数存储在数组中。

然后 Axios 将运行它们中的每一个。在 while 循环中,您可以看到函数被分配给要调用的变量。

让我们好奇——第 2 部分

发送 HTTP 请求不限于前端开发。在开发后端部分时,我们可能需要向外部服务器发送请求。您能否向我们解释一下如何以您的首选语言处理 HTTP 请求配置?欢迎在留言区分享您的想法。

在 Node.js 中处理 HTTP 请求

使用 Node.js,我们可以使用 JavaScript 开发后端部分。后端开发是关于处理 HTTP 请求,即:接收它们,解析它们,找到正确的答案,并响应客户端。Node.js 最常用的框架之一是 Express.js。

该框架使用中间件来完成上述任务。以下是 Express 官方页面中中间件的定义:

中间件函数是可以访问请求对象 (req)、响应对象 (res) 和应用程序请求-响应循环中的下一个中间件函数的函数。

您可以在下面看到中间件的示例。

var express = require('express');var app = express();
app.use('/users', function (req, res, next) {
// assume only authenticated users can access to /users route if(!req.user) { // non authenticated users res.json({status : "failed", message: "Please login!"}); return; }
// if users is authenticated, go to the next middleware next();});

中间件函数在 use 方法中传递。反过来,它接受另一个函数 next 作为参数。最后调用 next 函数,将控制权传递给堆栈中的以下中间件。

Express 因其简单性而广受欢迎并被广泛使用。“一个 Express 应用程序本质上是一系列中间件函数调用。” 尽管看起来微不足道,但 Express 的中间件可以帮助我们完成 Web 服务器的所有任务:记录请求、压缩响应、设置 cookie、防止 XSS 攻击……仅举几例。

让我们再次好奇!

HTTP 请求在其他后端框架中是如何处理的?您能将它与 Express 中间件进行比较吗?每种方法的优点/缺点是什么?你看,有很多问题要研究!

最后但并非最不重要的——JavaScript 中的回调地狱

如您所知,JavaScript 是单线程的。但它提供了一种有效的机制来处理长时间运行的任务。我们可以立即开始下一个任务,而不是等待任务完成,并定义前一个任务完成后我们需要做什么。这就是回调函数的来源——定义在长时间运行的任务后应该运行什么。

import { readFile } from 'fs';
readFile('/etc/passwd', (err, data) => { if (err) throw err; console.log(data);});

回调函数为我们提供了一个强大的工具来处理 I/O 绑定的应用程序。然而,任何好事如果被滥用都会变坏。您可以查看下面的示例。

fs.readdir(source, function (err, files) {  if (err) {    console.log('Error finding files: ' + err)  } else {    files.forEach(function (filename, fileIndex) {      console.log(filename)      gm(source + filename).size(function (err, values) {        if (err) {          console.log('Error identifying file size: ' + err)        } else {          console.log(filename + ' : ' + values)          aspect = (values.width / values.height)          widths.forEach(function (width, widthIndex) {            height = Math.round(width / aspect)            console.log('resizing ' + filename + 'to ' + height + 'x' + height)            this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {              if (err) console.log('Error writing file: ' + err)            })          }.bind(this))        }      })    })  }})

多个回调函数和 if/else 语句使代码难以理解。如果我们添加更多逻辑,它在未来可能变得不可维护。由于这个问题,引入了更新的功能。Promise 似乎可以帮助我们编写一个更简洁的程序。Async/await 关键字允许我们编写看起来像同步代码的异步代码。

总结

在这篇文章中,我向您展示了一些在 JavaScript 中使用“一流函数”的真实示例。

我们每天都使用此功能并认为这是理所当然的。通过这些例子,我希望您能看到这个特性为我们提供的一些很酷的东西。

我也给你留下了很多问题。好奇心是帮助我们成长的特征之一。

很高兴在评论区看到您的回答,让我们互相学习。您也可以将文章分享给其他语言的开发者进行讨论。

感谢您的阅读!


学习更多技能

请点击下方公众号

浏览 37
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报