JavaScript中高阶函数的强大功能(附例和用例)
找我中等
JavaScript在几乎所有存在的JavaScript应用程序中都使用函数。由于功能的原因,该语言能够实现许多强大功能,例如在医疗保健领域构建人工智能功能。
我将在本文中讨论的一个强大功能是利用函数,称为高阶函数。高阶函数是一个函数,它将另一个函数作为参数或返回一个函数作为返回值。我还将展示一些强大的示例和使用它们的用例,这是大多数教程都缺少的。但不用担心,你会在这篇文章中找到例子。
话虽如此,如果你一直在玩JavaScript,你可能已经听过这个术语。高阶函数在JavaScript中被广泛使用,它们存在于常用函数中 .map
, .filter
, .reduce
和 .forEach
。
如果您对JavaScript有点新兴,可能会对这些高阶函数在这些方法中的位置感到困惑。
当您将函数回调声明为这些数组方法的参数时,您会看到以下内容:
const arr = (1, 2, 3, 4, 5, 'six', 'seven', 'eight', 'nine', 'ten') // Duplicate the array arr.map(function(value) { return value }) // Return only the number types arr.filter(function(value) { return typeof value === 'number' }) // Log each value to the console arr.forEach(function(value) { console.log(value) }) // Add the numbers together, avoiding the string types arr.reduce(function(acc, value) { if (typeof value === 'number') { acc += value } return acc }, 0)
但是高阶函数不是你传递给类似方法的函数 .map
。方法如 .map
是高阶函数。
当我们提到高阶函数可以是将另一个函数作为参数的函数时,这正是它在传入函数时所做的事情。
这是一个功能完全类似的实现 .map
方法做:
function map(callback) { const result = () for (let index = 0; index < this.length; index++) { const currentItem = this(index) const returnValue = callback(currentItem, index, this) result.push(returnValue) } return result }
看一下代码片段 callback
参数是我们作为参数传递给的完全相同的函数 .map
我之前展示的方法:
// Duplicate the array arr.map(function(value) { return value })
更确切地说,让我将确切的代码重命名为与我们相同的名称 map
函数实现,以便您可以更清楚地看到它:
const callback = function(value) { return value } // Duplicate the array arr.map(callback) // is the same callback used in our .map implementation: function map(callback) { const result = () for (let index = 0; index < this.length; index++) { const currentItem = this(index) const returnValue = callback(currentItem, index, this) result.push(returnValue) } return result }
起初,在JavaScript中编写代码似乎是一种无用的方法。为什么要传递一个函数而烦恼返回另一个函数,当你可以避免所有这一切并在一个函数中同时执行所有操作时?
高阶函数带来的最大好处是可重用性和简单性。但他们也可以从编写漂亮的代码中受益。是的,JavaScript中存在丑陋的代码和漂亮的代码。
考虑到可重用性,它引入了一些非常强大的代码组合。
代码组成和强大的示例
现在我们知道代码中的高阶函数是什么样的,你可能想知道什么是一些用例以及它们在哪里开始发光。
假设我们有一个青蛙列表:
const frogsList = ( // Yes, these frogs are intelligent. They know how to use email { name: 'bobTheFrog', email: 'froggy@gmail.com', age: 2, gender: 'Male', widthOfTongue: 3, }, { name: 'hippoTheFrog', email: 'hippo@gmail.com', age: 10, gender: 'Male', widthOfTongue: 11, }, { name: 'sally', email: 'sallyLipstick@gmail.com', age: 5, gender: 'Female', widthOfTongue: 4, }, { name: 'george', email: 'georgeRoseBowl@gmail.com', age: 11, gender: 'Male', widthOfTongue: 3, }, { name: 'lisa', email: 'lisaLovesGeorgeForever@gmail.com', age: 19, gender: 'Female', widthOfTongue: 15, }, { name: 'kentucky', email: 'frogInKentucky@yahoo.com', age: 18, gender: 'Male', widthOfTongue: 13, }, )
要在没有更高阶函数的情况下将青蛙过滤到特定的性别类型,我们必须执行以下操作:
function filterGender(gender, frogs) { return frogs.filter(function(frog) { return frog.gender ==== gender }) } // filterGender in use const maleFrogs = filterGender('Male', frogsList)
这很好。但是,如果在应用程序中多次使用,则会很麻烦。如果我们有一个关于青蛙的巨大应用程序, filterGender
可能不止一次使用。
再迈出第二步
如果你要获取一个不同的青蛙名单,你必须打电话 filterGender
再次并重新声明您的性别作为过滤新列表的第一个参数:
function getFrogs() { // some logic and returns a new list of frogs } const newFrogs = getFrogs() const moreMaleFrogs = filterGender('Male', newFrogs) // Shucks, I have to write 'Male' again?
如果您从未听说过DRY原则,我强烈建议您了解它。由于第一个参数,我们的代码段违反了此规则。我们可以做得更好。
要解决这个问题,我们可以使用更高阶函数的概念。
function filterGender(gender) { return function(frogs) { return frogs.filter(function(frog) { return frog.gender === gender }) } }
现在,就像那样,我们可以将这个性别过滤器分配给变量,我们再也不必在过滤青蛙时声明相同的性别
const filterFemaleFrogs = filterGender('Female') const femaleFrogs = filterFemaleFrogs(frogsList)
但等等,这不是全部。我们通过编写它们获得了额外的好处。我们不仅可以再次为女性青蛙重新编写过滤器而受益,但我们现在还能够重新使用返回的功能从不同的青蛙名单中过滤同一性别
现在我们可以从多个青蛙列表中过滤掉女性,而无需编写尽可能多的代码:
const frogsList = ( // Yes, these frogs are intelligent. They know how to use email { name: 'bobTheFrog', email: 'froggy@gmail.com', age: 2, gender: 'Male', widthOfTongue: 3, }, { name: 'hippoTheFrog', email: 'hippo@gmail.com', age: 10, gender: 'Male', widthOfTongue: 11, }, { name: 'sally', email: 'sallyLipstick@gmail.com', age: 5, gender: 'Female', widthOfTongue: 4, }, { name: 'george', email: 'georgeRoseBowl@gmail.com', age: 11, gender: 'Male', widthOfTongue: 3, }, { name: 'lisa', email: 'lisaLovesGeorgeForever@gmail.com', age: 19, gender: 'Female', widthOfTongue: 15, }, { name: 'kentucky', email: 'frogInKentucky@yahoo.com', age: 18, gender: 'Male', widthOfTongue: 13, }, ) const frogsList2 = ( { name: 'abc', email: 'froggy@gmail.com', age: 2, gender: 'Male', widthOfTongue: 1, }, { name: '123', email: 'hippo@gmail.com', age: 10, gender: 'Male', widthOfTongue: 4, }, { name: 'joe', email: 'sallyLipstick@aol.com', age: 5, gender: 'Female', widthOfTongue: 6, }, { name: 'jennifer', email: 'georgeRoseBowl@aol.com', age: 11, gender: 'Female', widthOfTongue: 10, }, ) const frogsList3 = ( { name: 'professorHammick', email: 'froggy@gmail.com', age: 2, gender: 'Female', widthOfTongue: 1, }, { name: 'macintosh', email: 'hippo@gmail.com', age: 10, gender: 'Female', widthOfTongue: 6, }, { name: 'frogger', email: 'sallyLipstick@gmail.com', age: 5, gender: 'Female', widthOfTongue: 4, }, { name: 'frogNation', email: 'georgeRoseBowl@gmail.com', age: 11, gender: 'Female', widthOfTongue: 4, }, ) function gatherFemaleFrogsEverywhere(...frogLists) { const allFemaleFrogs = () const filterFemaleFrogs = filterGender('Female') frogLists.forEach(function(list) { allFemaleFrogs.push(...filterFemaleFrogs(list)) }) return allFemaleFrogs } const females = gatherFemaleFrogsEverywhere(frogsList, frogsList2, frogsList3)
进一步迈出第三步
如果您仍然不能确信JavaScript语言中的高阶函数有多强大,那么让我们继续这个示例来创建一个更通用的函数来创建更高级别的可重用性:
function filterFrogs(filter) { return function(frogs) { return frogs.filter(filter) } }
以前我们有能力为青蛙的性别制定可重复使用的功能。但是,我们可以通过抽象出逻辑来进一步发展 filter
功能,现在我们可以编写和重用不同的过滤功能
const filterMaleFrogs = filterFrogs(function(frog) { return frog.gender === 'Male' }) const filterAdultFrogs = filterFrogs(function(frog) { return frog.age >= 10 }) const filterFrogNamesThatStartWithHippo = filterFrogs(function(frog) { return frog.name.toLowerCase().startsWith('hippo') }) const filterGmailEmails = filterFrogs(function(frog) { return /gmail.com/i.test(frog.email) })
哇
以前我们有重新使用性别过滤器功能的惊人能力,而不必再次声明相同的性别类型,但现在我们有额外的能力创建和重新使用我们希望如何过滤青蛙的功能惊人
我们甚至可以同时使用它们:
function applyAllFilters(...filters) { return function(frogs) { let newFrogs = (...frogs) for (let index = 0; index < filters.length; index++) { const filter = filters(index) newFrogs = filter(newFrogs) } return newFrogs } } const applyFrogFilterers = applyAllFilters( filterMaleFrogs, filterAdultFrogs, filterFrogNamesThatStartWithHippo, filterGmailEmails, ) const combinedFrogsList = (...frogsList, ...frogsList2, ...frogsList3) const filteredFrogs = applyFrogFilterers(combinedFrogsList) console.log(filteredFrogs) /* result: { age: 10, email: "hippo@gmail.com", gender: "Male", name: "hippoTheFrog", widthOfTongue: 11 } */
再来一次
我们的 applyAllFilters
功能很好地完成了这项工作。然而,对于巨大的青蛙名单,它可能会成为一项繁重的任务,因为它运行 filter
多次获得最终结果。
我们可以再次使用高阶函数的概念来创建一个简单的,可重用的高阶函数,该函数能够通过同时应用过滤器使一个遍历整个青蛙列表。
更清楚的是,看看for循环代码并尝试看看幕后真正发生的事情:
function applyAllFilters(...filters) { return function(frogs) { let newFrogs = (...frogs) for (let index = 0; index < filters.length; index++) { const filter = filters(index) newFrogs = filter(newFrogs) } return newFrogs } }
我想让你看到的一行是这样的:
newFrogs = filter(newFrogs)
那行代码与代码行相同 return frogs.filter(filter)
在这个功能:
function filterFrogs(filter) { return function(frogs) { return frogs.filter(filter) } }
这是一个问题,因为filter方法会创建一个新数组。当我们写下这个:
const applyFrogFilterers = applyAllFilters( filterMaleFrogs, filterAdultFrogs, filterFrogNamesThatStartWithHippo, filterGmailEmails, )
我们正在调用过滤方法4次。换句话说,我们正在使JavaScript在内存中创建四个不同的数组,以获得最终结果。
那么我们怎样才能使JavaScript只创建一个数组来获得相同的结果呢?
你猜到了。使用更高阶函数
function composeFrogFilterers(...fns) { return function(frogs) { return fns.reduce(function(accumulatedFrogs, fn) { return fn(accumulatedFrogs) }, frogs) } } const applyFrogFilterers = composeFrogFilterers( filterMaleFrogs, filterAdultFrogs, filterFrogNamesThatStartWithHippo, filterGmailEmails, ) const allFilteredFrogs = applyFrogFilterers(combinedFrogsList) console.log(allFilteredFrogs) /* result: { age: 10, email: "hippo@gmail.com", gender: "Male", name: "hippoTheFrog", widthOfTongue: 11 } */
结论
我希望您确信高阶函数有多强大,通过阅读本文,您可以更深入地了解此概念的用例了解更多未来
找我中等