4 个JavaScript 中一流函数的日常用例
共 5869字,需浏览 12分钟
·
2021-12-24 19:27
英文 | https://medium.com/codex/4-daily-use-cases-of-first-class-functions-in-javascript-17b7079a6217
翻译 | 杨小爱
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 interceptors
axios.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 interceptors
var 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 中使用“一流函数”的真实示例。
我们每天都使用此功能并认为这是理所当然的。通过这些例子,我希望您能看到这个特性为我们提供的一些很酷的东西。
我也给你留下了很多问题。好奇心是帮助我们成长的特征之一。
很高兴在评论区看到您的回答,让我们互相学习。您也可以将文章分享给其他语言的开发者进行讨论。
感谢您的阅读!
学习更多技能
请点击下方公众号