充分利用高阶函数——JavaScript 示例
函数不仅仅是避免代码重复的一种方式。函数作为可在运行时操作和修改的对象,其强大的概念能够提升应用程序的灵活性和可扩展性。学习如何使用和编写高阶函数,可以提高您的工作质量。
内容
- 介绍
- 函数作为子程序 vs 函数作为存储计算
- 什么是高阶函数?
- 高阶函数的一些应用案例
- 对现有函数设置附加约束
- 创建逻辑相同但参数不同的其他函数
- 在数据结构中使用另一个函数
- 列表的高阶函数
- 完整示例
- 结论
介绍
函数作为子程序 vs 函数作为存储计算
从简单的角度来看,函数用于减少代码重复,将一些接受参数且可能(也可能不)返回值的逻辑分离出来。然而,从数学和函数式编程范式出发,我们获得了更强大的函数概念,它是一种可应用的计算工具。
这种细微差别有趣之处在于,计算结果也可以存储为数据。在一些动态语言(例如 JavaScript 和 Python)中,这意味着你可以像操作对象一样操作函数,也就是所谓的“一等公民”。
什么是高阶函数?
简而言之,高阶函数就是以其他函数为对象的函数。
- 以其他函数作为参数的函数。
- 返回值也是另一个函数的函数。
高阶函数的一些应用案例
在继续之前,快速提醒一下,
x => y语法等价于function(x){ return y }。
对现有函数设置附加约束
例如:限制另一个函数输出范围的函数。
const clampedFunc = function(fun, min, max){
return (...args) => Math.max(min, Math.min(max, fun(...args)))
}
// Demonstration
squared1 = x => x*x
squared2 = clampedFunc(squared1, 25, 100)
squared1(3) // 9
squared1(6) // 36
squared1(12) // 144
squared2(3) // 25
squared2(6) // 36
squared2(12) // 100
创建逻辑相同但参数不同的其他函数
示例:用于创建线性函数的函数。
const linear = function(m, n){
return (x) => m*x + n
}
// Demonstration
f1 = linear(1, 2)
f1(10) // 12
f1(20) // 22
f2 = linear(2, -5)
f2(7) // 9
f2(8) // 11
在数据结构中使用另一个函数
这实际上是最常见的用例。事实上,大多数现代编程语言的标准库中都包含了这类函数。我们将在下一节中看到一些例子。
列表的高阶函数
forEach对列表中的每个元素应用一个函数,并忽略返回值(如果有)。map对列表中的每个元素应用一个函数,并返回所有返回值的列表。在其他语言中,它被称为 apply。reduce将一个接受两个参数的函数应用于前两个元素。然后,将函数再次应用于结果和第三个元素。接着,将函数应用于结果和第四个元素,依此类推。简而言之,就是对所有元素累加函数值。在其他语言中,这被称为折叠(fold)。
以下情况下,“条件”指的是返回布尔值的函数。
some如果至少有一个元素满足某个条件,则返回 true。在其他语言中,它被称为 any。every如果列表中的所有元素都满足某个条件,则返回 true。filter返回一个仅包含满足条件的元素的列表。
例如:
nums = [ 1, 2, 3, 4, 5 ]
words = [ 'how', 'are', 'you' ]
nums.forEach(x => console.log("- " + x))
// returns nothing but prints nums as a bullet list
nums.map( x => x*3 )
// [ 3, 6, 9, 12, 15 ]
words.reduce( (x, y) => x + ' ' + y )
// 'how are you'
nums.some( x => x > 5 )
// false
words.every( x => x.length == 3 )
// true
nums.filter(x => x % 2 == 0)
// [ 2, 4 ]
完整示例
让我们把所学知识应用到一个具体案例中。
// Function to concatenate other two functions (this is called composition)
const compose = function (f1, f2){
return (...args) => f2(f1(...args))
}
// Function to compose any number of functions (general composition)
const gCompose = function(fs){
return fs.reduce(compose)
}
// Function to generate custom formatter functions
const custom_fmt = function(text, variable){
return (value) => text.replace(variable, value)
}
// Convert USD to Euros
const usd2eur = function(x){
return x/1.2
}
// Fix the precision a number to 2
const fix2 = function(x){
return x.toFixed(2)
}
// Store the functions in the order we want to apply them
myComputation = [usd2eur, fix2, custom_fmt("Cost in EUR: x", "x")]
// Compose them into a single function
myComputationFunc = gCompose(myComputation)
// Apply the computation we just created to each element of our list and print the result
usdCosts = [2.50, 10.99, 3.3, 5.72]
usdCosts.map(myComputationFunc).forEach(x => console.log('-',x))
/* Console output
- Cost in EUR: 2.08
- Cost in EUR: 9.16
- Cost in EUR: 2.75
- Cost in EUR: 4.77
*/
结论
举个简单的例子,这种方法虽然有些夸张,但足以说明问题。了解高阶函数的工作原理及其可能性非常重要:
- 尽量减少代码中循环和分支的使用,提高代码的可读性。
- 帮助您抽象和概括流程,使程序更加灵活和可扩展。
- 减少大型应用程序的代码,对可能需要更多或更少处理的数据应用原子计算,或者在执行过程中所需的转换发生变化。
希望这篇文章对您有所帮助,欢迎在评论区留言分享您的想法!
推荐阅读

