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 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 关键字允许我们编写看起来像同步代码的异步代码。
我们从以下三个方面,对比纯静态和伪静态两种静态页面生成方式,逐一展开分析。
用JS的正则表达式如何判断输入框内为中文或者是英文数字,或者是三者混编
css制作扇形
纯CSS3文字Loading动画特效
PhpStorm 2022.1 EAP 3 在 PHPDoc 和属性中添加了对多行和嵌套数组形状的完全支持:在这种情况下,可以使用数组形状注释定义数组结构,以获得键的代码补全并推断值的类型。
PHP作为Web界第一大语言近年来热度不够,但是这几年的进步和成长却没有中断。在2022伊始,我们来一起学习一下目前PHP的现状以及最新版本带来的特性。
Linux程序前台后台切换:在Linux终端运行命令的时候,在命令末尾加上 & 符号,就可以让程序在后台运行Ubuntu$">root@Ubuntu$ ./tcpserv01 &
Python 的正则表达式支持 多行模式,将每行文字分别匹配。然而各种操作系统里,换行符的表示法各不相同,会导致 Python 不能正确使用多行模式。