JavaScript 中的函数式编程
函数式编程真的是一个非常实用的工具。对我来说,它改变了我设计各种问题解决方案的方式。
什么是函数式编程?
函数式编程是一种编程范式,其中一切都是函数,目标是让一切保持直观和数学化。变量应保持不可变,至少在函数级别是这样,并且应按其定义执行,而不会产生任何副作用。函数printObject
不应该向其传入的对象添加字段。
然而,最重要的部分可以从范式的名称“函数式编程”中推断出来。在函数式编程中,函数是最重要的。通常,这表现为允许它们存储在变量中,从而允许它们在许多可以使用其他类型变量的地方使用。
市面上有很多不同的纯函数式语言,但本文我将重点介绍 JavaScript。我将把它分为两部分:第一部分重点介绍允许使用函数式编程的内置工具;第二部分介绍如何创建自己的函数和使用函数式编程的工具。
JavaScript 中的函数式编程
JavaScript 非常注重函数优先。将函数赋值给变量是创建变量的重要方式!
const printHello = function() {
console.log("Hello!");
}
printHello();
不仅如此,创建函数的方法还有很多。其中一种方法是像上面一样使用 function 关键字,或者在函数名称前使用 function 关键字。
function add(firstNumber, secondNumber) {
return firstNumber + secondNumber;
}
console.log(add(2, 4)); // Prints "6"
创建函数的第三种方法是使用箭头符号。
const yellToConsole = (message) => {
console.log(message.toUpperCase());
}
yellToConsole("Hello!"); // Prints "HELLO!"
JavaScript 中的箭头函数和普通函数之间存在一些细微差别,这些差别出现在箭头函数的 MDN 页面标题中。
因为这种方式并不是JavaScript 中唯一的方法,所以 JavaScript 并不是一个纯粹的函数式编程语言,但是函数式编程的主要方面——函数可以分配给变量——是该语言的重要组成部分。
标准库
我在 JavaScript 标准库中看到的函数式编程的两个主要地方是数组函数和回调,尤其是当扩展到包含许多常用的库(如Lodash)时。
每个数组都附带一些附加功能。
Map
map 函数以函数作为参数,并将数组转换为其他内容。
const numberList = [1, 2, 3, 4];
const stringList = numberList.map((number) => number.toString()); // ["1", "2", "3", "4"]
在这里,我创建了一个箭头函数作为它的参数,但是您也可以通过以其他方式声明一个函数,然后将该函数的引用传递给 map 函数,轻松完成相同的操作。
const numberList = [1, 2, 3, 4];
function square(number) {
return number * number;
}
const squareList = numberList.map(square); // [1, 4, 9, 16]
Reduce
Reduce 函数执行类似的转换操作,并添加了将数组合并的效果。它用于将数组转换为单个值。
const numberList = [1, 2, 3, 4];
const sum = numberList.reduce((currentSum, number) => currentSum + number, 0); // 10
注意到我们是如何获取currentSum
函数参数的了吗?它被称为累加器 (accumulator),表示迄今为止已组合的所有内容。0
函数调用的末尾还有一个 ,它告诉 JavaScript 累加器应该以什么开头。如果我们要根据列表中的元素构建字符串,则累加器可以是一个空字符串。
Filter
Filter 接受一个函数,该函数用于判断某个元素是否应该保留在列表中,然后返回一个包含所有应该保留元素的新列表。这个函数filter
被称为谓词函数,因为它接受一个值,并根据某种逻辑返回“true
或” 。false
const numberList = [1, 2, 3, 4];
const evens = numberList.filter((number) => number % 2 === 0); // [2, 4]
过滤器还可以用于从我们无法修改的列表中删除项目。你觉得我们应该如何实现这一点?
Some
filter
Some 是和的组合reduce
。它用于判断列表中是否有任何值与谓词匹配。
const numberList = [1, 2, 3, 4];
const isOneInList = numberList.some((number) => number === 1); // true
回调
回调用于在某件事完成时执行处理。它们可以链接在一起以进行复杂的处理,但当它们嵌套多层时可能会变得混乱。这种混乱正是async
/await
的设计动机之一。块内的函数在Promisesthen
中就是回调。
fetch('https://api.github.com/gists/e59384900942ee13af189bf92c7adfef')
.then(response => response.json())
.then(json => console.log(json.description)); // Prints "Predication in C# collections."
每个.then
调用都接受一个函数,并且其返回的内容都会传递给下一个.then
调用。
自己动手——编写一个简单的 Some 函数
所以知道如何使用它们很重要,但让我们自己写一个。一个简单的方法是重新创建some
我上面展示的函数。首先,我要为这个函数确定一个签名。它需要一个列表和一个函数作为参数。我将使用 JSDoc 为它们编写一个规范。
/**
* @callback anyMatch~predicate
*
* A predicate that applies to any item.
*
* @param {any} item The item to check this condition on.
* @returns {boolean} Whether this condition passed or not.
*/
/**
* Imitation of the array 'some' function.
*
* @param {any[]} list The list to process.
* @param {anyMatch~predicate} predicate The predicate to apply to each item.
* @returns {boolean} Whether any item in this list matched this condition.
*/
const anyMatch = function(list, predicate) {
};
接下来我们可以循环检查每个项目是否符合谓词:
const anyMatch = function(list, predicate) {
for(const item of list) {
if(predicate(item)) { // The item matched the predicate.
return true;
}
}
return false; // Nothing matched the predicate.
};
注意到了吗,我们可以predicate
像调用其他函数一样调用它吗?就是这样。现在我们来运行一些测试:
console.log(anyMatch([1, 2, 3, 4], (item) => item === 1)); // Prints "true"
console.log(anyMatch([1, 2, 3, 4], (item) => item === 5)); // Prints "false"
而且它真的有效!希望以上内容至少能给你一些启发。你认为这种方法在哪些常见场景下有效?请在评论区留言告诉我。
鏂囩珷鏉ユ簮锛�https://dev.to/hunter/function-programming-in-javascript-3j0p