原文:Higher Order Functions: Using Filter, Map and Reduce for More Maintainable Code
作者:Guido Schmitz
譯者:JeLewine
高階函數(shù)可以幫助你增強(qiáng)你的JavaScript流纹,讓你的代碼更具有聲明性堆缘。簡(jiǎn)單來(lái)說(shuō)掏颊,就是簡(jiǎn)單趣效,簡(jiǎn)練瘦癌,可讀。
知道什么時(shí)候和怎樣使用高階函數(shù)是至關(guān)重要的跷敬。它們可以讓你的代碼更容易理解和具有更好的可維護(hù)性讯私。它們也可以讓你很輕松的進(jìn)行函數(shù)間的組合。我們叫它復(fù)合函數(shù)干花,不過(guò)我不會(huì)在這里進(jìn)行詳細(xì)的介紹妄帘。在本文中,我將介紹JavaScript中三個(gè)最常用的高階函數(shù):.filter()
池凄,.map()
抡驼,.reduce
。
Filter
想象一下你正在編寫(xiě)一段代碼:有一個(gè)寫(xiě)滿不同人信息的列表肿仑,不過(guò)你想要過(guò)濾出一個(gè)大于等于18歲人的列表致盟。
我們的列表看起來(lái)就像下面這樣:
const people = [
{ name: ‘John Doe’, age: 16 },
{ name: ‘Thomas Calls’, age: 19 },
{ name: ‘Liam Smith’, age: 20 },
{ name: ‘Jessy Pinkman’, age: 18 },
];
我們先來(lái)看看第一個(gè)高階函數(shù)是如何篩選出大于等于18歲人的栗子。為了簡(jiǎn)潔尤慰,我將使用ES6標(biāo)準(zhǔn)中的箭頭函數(shù)馏锡。這是一種非常簡(jiǎn)潔的定義函數(shù)的方式,可以讓我們不必再寫(xiě)function
和return
伟端,以及一些括號(hào)杯道、大括號(hào)和分號(hào)。
const peopleAbove18 = (collection) => {
const results = [];
for (let i = 0; i < collection.length; i++) {
const person = collection[i];
if (person.age >= 18) {
results.push(person);
}
}
return results;
};
那現(xiàn)在如果我們想要篩選出18~20歲之間的人呢责蝠?
const peopleBetween18And20 = (collection) => {
const results = [];
for (let i = 0; i < collection.length; i++) {
const person = collection[i];
if (person.age >= 18 && person.age <= 20) {
results.push(person);
}
}
return results;
};
你可能已經(jīng)意識(shí)到了這里有許多重復(fù)的代碼党巾。我們可以抽象出一個(gè)通用的解決方案。這兩個(gè)函數(shù)有一些共同點(diǎn)霜医。它們都在一個(gè)列表中進(jìn)行迭代齿拂,并且在給定的條件下進(jìn)行過(guò)濾。
"高階函數(shù)是一個(gè)將一個(gè)或多個(gè)函數(shù)作為參數(shù)的函數(shù)"——ClojureBridge
我們可以通過(guò)使用更具聲明性的.filter()
方法來(lái)改進(jìn)我們之前的函數(shù)肴敛。
const peopleAbove18 = (collection) => {
return collection
.filter((person) => person.age >= 18);
}
太棒了!我們通過(guò)使用高階函數(shù)減少了許多額外的代碼署海。同時(shí)也讓我們的代碼更具可讀性吗购。我們不在乎如何過(guò)濾東西,我們只是希望它被過(guò)濾砸狞。這篇文章稍后會(huì)介紹組合函數(shù)捻勉。
Map
讓我們拿著剛剛的人員名單和一個(gè)其中喜歡喝咖啡的人員名單。
const coffeeLovers = [‘John Doe’, ‘Liam Smith’, ‘Jessy Pinkman’];
用命令式的實(shí)現(xiàn)方式就像下面這樣:
const addCoffeeLoverValue = (collection) => {
const results = [];
for (let i = 0; i < collection.length; i++) {
const person = collection[i];
if (coffeeLovers.includes(person.name)) {
person.coffeeLover = true;
} else {
person.coffeeLover = false;
}
results.push(person);
}
return results;
};
我們可以利用.map()
來(lái)讓代碼更具有聲明性:
const incrementAge = (collection) => {
return collection.map((person) => {
person.coffeeLover = coffeeLovers.includes(person.name);
return person;
});
};
再說(shuō)一遍刀森,.map()
是一個(gè)高階函數(shù)贯底。它允許我們將一個(gè)函數(shù)作為參數(shù)傳遞。
Reduce
我敢打賭撒强,當(dāng)你知道什么時(shí)候和怎樣使用它的時(shí)候,你會(huì)喜歡上這個(gè)函數(shù)笙什。.reduce()
很酷飘哨,上面提到的的大部分函數(shù)都可以通過(guò)它來(lái)實(shí)現(xiàn)。
讓我們先舉一個(gè)簡(jiǎn)單的栗子琐凭。我們想計(jì)算所有人年齡的和芽隆。當(dāng)然了,我們還是會(huì)首先看看如何用命令式的方式實(shí)現(xiàn)统屈。它基本上就是通過(guò)循環(huán)來(lái)增加總年齡變量胚吁。
const sumAge = (collection) => {
let num = 0;
collection.forEach((person) => {
num += person.age;
});
return num;
}
接下來(lái)是使用.reduce()
的聲明式方法:
const sumAge = (collection) => collection.reduce((sum, person) => {
return sum + person.age;
}, 0);
我們甚至可以使用.reduce()
來(lái)創(chuàng)建我們自己的.map()
和.filter()
。
const map = (collection, fn) => {
return collection.reduce((acc, item) => {
return acc.concat(fn(item));
}, []);
}
const filter = (collection, fn) => {
return collection.reduce((acc, item) => {
if (fn(item)) {
return acc.concat(item);
}
return acc;
}, []);
}
一開(kāi)始這一塊兒可能比較難理解愁憔。不過(guò)腕扶,.reduce()
做的基本上就是以一個(gè)集合和一個(gè)定義了初始值的變量開(kāi)始。然后吨掌,遍歷該集合并將值添加到變量中去半抱。
組合map,filter和reduce
太好了膜宋,這些函數(shù)我們都有了窿侈。而且很重要的一點(diǎn)是,他們都存在于JavaScript的數(shù)組原型上秋茫。這意味著我們可以同時(shí)使用它們史简。這可以讓我們輕松創(chuàng)建各種可復(fù)用的函數(shù),減少編寫(xiě)某些功能所需要的代碼量肛著。
我們已經(jīng)討論過(guò)了如何利用.filter()
來(lái)過(guò)濾出大于等于18歲的人圆兵;利用.map()
來(lái)添加coffeeLover
屬性;通過(guò).reduce()
來(lái)計(jì)算所有人年齡的和〔咂現(xiàn)在衙傀,我們寫(xiě)一點(diǎn)代碼將這三個(gè)步驟合并起來(lái)。
const people = [
{ name: ‘John Doe’, age: 16 },
{ name: ‘Thomas Calls’, age: 19 },
{ name: ‘Liam Smith’, age: 20 },
{ name: ‘Jessy Pinkman’, age: 18 },
];
const coffeeLovers = [‘John Doe’, ‘Liam Smith’, ‘Jessy Pinkman’];
const ageAbove18 = (person) => person.age >= 18;
const addCoffeeLoverProperty = (person) => {
person.coffeeLover = coffeeLovers.includes(person.name);
return person;
}
const ageReducer = (sum, person) => {
return sum + person.age;
}, 0);
const coffeeLoversAbove18 = people
.filter(ageAbove18)
.map(addCoffeeLoverProperty);
const totalAgeOfCoffeeLoversAbove18 = coffeeLoversAbove18
.reduce(ageReducer);
const totalAge = people
.reduce(ageReducer);
如果你用命令式方法的話萨咕,你最后會(huì)寫(xiě)一堆重復(fù)代碼统抬。
通過(guò).map()
,.reduce()
和.filter()
來(lái)創(chuàng)建函數(shù)的思維將會(huì)極大的提高你的代碼質(zhì)量。而且可以增加可讀性聪建。你根本不必在意函數(shù)內(nèi)到底發(fā)生了什么钙畔,它非常容易理解。