真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

JavaScript中map、reduce、filter和find的示例分析

這篇文章將為大家詳細講解有關(guān)JavaScript中map、reduce、filter和find的示例分析,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

公司主營業(yè)務(wù):網(wǎng)站設(shè)計、成都網(wǎng)站設(shè)計、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)是一支青春激揚、勤奮敬業(yè)、活力青春激揚、勤奮敬業(yè)、活力澎湃、和諧高效的團隊。公司秉承以“開放、自由、嚴謹、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團隊有機會用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)推出鯉城免費做網(wǎng)站回饋大家。

循環(huán)

在 JavaScript 中,至少有四、五種實現(xiàn)循環(huán)的方法,最基礎(chǔ)的是 while 循環(huán)。我們首先先創(chuàng)建一個示例函數(shù)和數(shù)組:

// oodlify :: String -> String
function oodlify(s) {
 return s.replace(/[aeiou]/g, 'oodle');
}

const input = [
 'John',
 'Paul',
 'George',
 'Ringo',
];

現(xiàn)在有了一個數(shù)組,我們想要用 oodlify 函數(shù)處理每一個元素。如果用 while 循環(huán),就類似于這樣:

let i = 0;
const len = input.length;
let output = [];
while (i < len) {
 let item = input[i];
 let newItem = oodlify(item);
 output.push(newItem);
 i = i + 1;
}

注意這里發(fā)生的事情,我們用了一個初始值為 0 的計數(shù)器 i,每次循環(huán)都會自增。而且每次循環(huán)中都和 len 進行比較以保證循環(huán)特定次數(shù)以后終止循環(huán)。這種利用計數(shù)器進行循環(huán)控制的模式太常用了,所以 JavaScript 提供了一種更加簡潔的寫法: for 循環(huán),寫起來如下:

const len = input.length;
let output = [];
for (let i = 0; i < len; i = i + 1) {
 let item = input[i];
 let newItem = oodlify(item);
 output.push(newItem);
}

這一結(jié)構(gòu)非常有用,while循環(huán)非常容易把自增的 i 給忘掉,進而引起無限循環(huán);而for循環(huán)把和計數(shù)器相關(guān)的代碼都放到了上面,這樣你就不會忘掉自增 i,這確實是一個很好的改進?,F(xiàn)在回到原來的問題,我們目標是在數(shù)組的每個元素上運行 oodlify() 函數(shù),并且將結(jié)果放到一個新的數(shù)組中。

對一個數(shù)組中每個元素都進行操作的這種模式也是非常普遍的。因此在 ES2015 中,引入了一種新的循環(huán)結(jié)構(gòu)可以把計數(shù)器也簡化掉: for...of 循環(huán)。每一次返回數(shù)組的下一個元素給你,代碼如下:

let output = [];
for (let item of input) {
 let newItem = oodlify(item);
 output.push(newItem);
}

這樣就清晰很多了,注意這里計數(shù)器和比較都不用了,你甚至都不用把元素從數(shù)組里面取出來。for...of 幫我們做了里面的臟活累活。如果現(xiàn)在用 for...of 來代替所有的 for 循環(huán),其實就可以很大程度上降低復(fù)雜性。但是,我們還可以做進一步的優(yōu)化。

mapping

for...of 循環(huán)比 for 循環(huán)更清晰,但是依然需要一些配置性的代碼。如不得不初始化一個 output 數(shù)組并且每次循環(huán)都要調(diào)用 push() 函數(shù)。但有辦法可以讓代碼更加簡潔有力,我們先擴展一下問題。

如果有兩個數(shù)組需要調(diào)用 oodlify 函數(shù)會怎么樣?

const fellowship = [
 'frodo',
 'sam',
 'gandalf',
 'aragorn',
 'boromir',
 'legolas',
 'gimli',
];

const band = [
 'John',
 'Paul',
 'George',
 'Ringo',
];

很容易想到的方法是對每個數(shù)組都做循環(huán):

let bandoodle = [];
for (let item of band) {
 let newItem = oodlify(item);
 bandoodle.push(newItem);
}

let floodleship = [];
for (let item of fellowship) {
 let newItem = oodlify(item);
 floodleship.push(newItem);
}

這確實ok,有能正確執(zhí)行的代碼,就比沒有好。但是重復(fù)的代碼太多了——不夠“DRY”。我們來重構(gòu)它以降低重復(fù)性,創(chuàng)建一個函數(shù):

function oodlifyArray(input) {
 let output = [];
 for (let item of input) {
  let newItem = oodlify(item);
  output.push(newItem);
 }
 return output;
}

let bandoodle = oodlifyArray(band);
let floodleship = oodlifyArray(fellowship);

這看起來好多了,可是如果我們想使用另外一個函數(shù)該怎么辦?

function izzlify(s) {
 return s.replace(/[aeiou]+/g, 'izzle');
}

上面的 oodlifyArray() 一點用都沒有了。但如果再創(chuàng)建一個 izzlifyArray() 函數(shù)的話,代碼又重復(fù)了。不管那么多,先寫出來看看什么效果:

function oodlifyArray(input) {
 let output = [];
 for (let item of input) {
  let newItem = oodlify(item);
  output.push(newItem);
 }
 return output;
}

function izzlifyArray(input) {
 let output = [];
 for (let item of input) {
  let newItem = izzlify(item);
  output.push(newItem);
 }
 return output;
}

這兩個函數(shù)驚人的相似。那么是不是可以把它們抽象成一個通用的模式呢?我們想要的是:給定一個函數(shù)和一個數(shù)組,通過這個函數(shù),把數(shù)組中的每一個元素做操作后放到新的數(shù)組中。我們把這個模式叫做 map 。一個數(shù)組的 map 函數(shù)如下:

function map(f, a) {
 let output = [];
 for (let item of a) {
  output.push(f(item));
 }
 return output;
}

這里還是用了循環(huán)結(jié)構(gòu),如果想要完全擺脫循環(huán)的話,可以做一個遞歸的版本出來:

function map(f, a) {
 if (a.length === 0) { return []; }
 return [f(a[0])].concat(map(f, a.slice(1)));
}

遞歸解決方法非常優(yōu)雅,僅僅用了兩行代碼,幾乎沒有縮進。但是通常并不提倡于在這里使用遞歸,因為在較老的瀏覽器中的遞歸性能非常差。實際上,map 完全不需要你自己去手動實現(xiàn)(除非你自己想寫)。map 模式很常用,因此 JavaScript 提供了一個內(nèi)置 map 方法。使用這個 map 方法,上面的代碼變成了這樣:

let bandoodle  = band.map(oodlify);
let floodleship = fellowship.map(oodlify);
let bandizzle  = band.map(izzlify);
let fellowshizzle = fellowship.map(izzlify);

可以注意到,縮進消失,循環(huán)消失。當然循環(huán)可能轉(zhuǎn)移到了其他地方,但是我們已經(jīng)不需要去關(guān)心它們了。現(xiàn)在的代碼簡潔有力,完美。

為什么這個代碼這么簡單呢?這可能是個很傻的問題,不過也請思考一下。是因為短嗎?不是,簡潔并不代表不復(fù)雜。它的簡單是因為我們把問題分離了。有兩個處理字符串的函數(shù): oodlify 和 izzlify,這些函數(shù)并不需要知道關(guān)于數(shù)組或者循環(huán)的任何事情。同時,有另外一個函數(shù):map ,它來處理數(shù)組,它不需要知道數(shù)組中元素是什么類型的,甚至你想對數(shù)組做什么也不用關(guān)心。它只需要執(zhí)行我們所傳遞的函數(shù)就可以了。把對數(shù)組的處理中和對字符串的處理分離開來,而不是把它們都混在一起。這就是為什么說上面的代碼很簡單。

reducing

現(xiàn)在,map 已經(jīng)得心應(yīng)手了,但是這并沒有覆蓋到每一種可能需要用到的循環(huán)。只有當你想創(chuàng)建一個和輸入數(shù)組同樣長度的數(shù)組時才有用。但是如果你想要向數(shù)組中增加幾個元素呢?或者想找一個列表中的最短字符串是哪個?其實有時我們對數(shù)組進行處理,最終只想得到一個值而已。

來看一個例子,現(xiàn)在一個數(shù)組里面存放了一堆超級英雄:

const heroes = [
 {name: 'Hulk', strength: 90000},
 {name: 'Spider-Man', strength: 25000},
 {name: 'Hawk Eye', strength: 136},
 {name: 'Thor', strength: 100000},
 {name: 'Black Widow', strength: 136},
 {name: 'Vision', strength: 5000},
 {name: 'Scarlet Witch', strength: 60},
 {name: 'Mystique', strength: 120},
 {name: 'Namora', strength: 75000},
];

現(xiàn)在想找最強壯的超級英雄。使用 for...of 循環(huán),像這樣:

let strongest = {strength: 0};
for (hero of heroes) {
 if (hero.strength > strongest.strength) {
  strongest = hero;
 }
}

雖然這個代碼可以正確運行,可是實在太爛了。看這個循環(huán),每次都保存到目前為止最強的英雄。繼續(xù)提需求,接下來我們想要所有超級英雄的總強度:

let combinedStrength = 0;
for (hero of heroes) {
 combinedStrength += hero.strength;
}

在這兩個例子中,都在循環(huán)開始之前初始化了一個變量。然后在每一次的循環(huán)中,處理一個數(shù)組元素并且更新這個變量。為了使這種循環(huán)套路變得更加明顯一點,現(xiàn)在把數(shù)組中間的部分抽離到一個函數(shù)當中。并且重命名這些變量,以進一步突出相似性。

function greaterStrength(champion, contender) {
 return (contender.strength > champion.strength) ? contender : champion;
}

function addStrength(tally, hero) {
 return tally + hero.strength;
}

const initialStrongest = {strength: 0};
let working = initialStrongest;
for (hero of heroes) {
 working = greaterStrength(working, hero);
}
const strongest = working;

const initialCombinedStrength = 0;
working = initialCombinedStrength;
for (hero of heroes) {
 working = addStrength(working, hero);
}
const combinedStrength = working;

用這種方式來寫,兩個循環(huán)變得非常相似了。它們兩個之間唯一的區(qū)別是調(diào)用的函數(shù)和初始值不同。兩個的功能都是對數(shù)組進行處理,最終得到一個值。所以,我們創(chuàng)建一個 reduce 函數(shù)來封裝這個模式。

function reduce(f, initialVal, a) {
 let working = initialVal;
 for (item of a) {
  working = f(working, item);
 }
 return working;
}

reduce 模式在 JavaScript 中也是很常用的,因此 JavaScript 為數(shù)組提供了內(nèi)置的方法,不需要自己來寫。通過內(nèi)置方法,代碼就變成了:

const strongestHero = heroes.reduce(greaterStrength, {strength: 0});
const combinedStrength = heroes.reduce(addStrength, 0);

ok,如果足夠細心的話,你會注意到上面的代碼其實并沒有短很多。不過也確實比自己手寫的 reduce 代碼少寫了幾行。但是我們的目標并不是使代碼變短或者少寫,而是降低代碼復(fù)雜度。現(xiàn)在的復(fù)雜度降低了嗎?我會說是的。把處理每個元素的代碼和處理循環(huán)代碼分離開來了,這樣代碼就不會互相糾纏在一起了,降低了復(fù)雜度。

reduce 方法乍一看可能覺得非常基礎(chǔ)。我們舉的 reduce 大部分也比如做加法這樣的簡單例子。但是沒有人說 reduce 方法只能返回基本類型,它可以是一個 object 類型,甚至可以是另一個數(shù)組。當我第一次意識到這個問題的時候,自己也是豁然開朗。所以其實可以用 reduce 方法來實現(xiàn) map 或者 filter,這個留給讀者自己做練習。

filtering

現(xiàn)在我們有了 map 處理數(shù)組中的每個元素,有了 reduce 可以處理數(shù)組最終得到一個值。但是如果想獲取數(shù)組中的某些元素該怎么辦?我們來進一步探索,現(xiàn)在增加一些屬性到上面的超級英雄數(shù)組中:

const heroes = [
 {name: 'Hulk', strength: 90000, sex: 'm'},
 {name: 'Spider-Man', strength: 25000, sex: 'm'},
 {name: 'Hawk Eye', strength: 136, sex: 'm'},
 {name: 'Thor', strength: 100000, sex: 'm'},
 {name: 'Black Widow', strength: 136, sex: 'f'},
 {name: 'Vision', strength: 5000, sex: 'm'},
 {name: 'Scarlet Witch', strength: 60, sex: 'f'},
 {name: 'Mystique', strength: 120, sex: 'f'},
 {name: 'Namora', strength: 75000, sex: 'f'},
];

ok,現(xiàn)在有兩個問題,我們想要:

找到所有的女性英雄;
找到所有能量值大于500的英雄。
使用普通的 for...of 循環(huán),會得到如下代碼:

let femaleHeroes = [];
for (let hero of heroes) {
 if (hero.sex === 'f') {
  femaleHeroes.push(hero);
 }
}

let superhumans = [];
for (let hero of heroes) {
 if (hero.strength >= 500) {
  superhumans.push(hero);
 }
}

邏輯嚴密,看起來還不錯?但是里面又出現(xiàn)了重復(fù)的情況。實際上,區(qū)別在于 if 的判斷語句,那么能不能把 if 語句重構(gòu)到一個函數(shù)中呢?

function isFemaleHero(hero) {
 return (hero.sex === 'f');
}

function isSuperhuman(hero) {
 return (hero.strength >= 500);
}

let femaleHeroes = [];
for (let hero of heroes) {
 if (isFemaleHero(hero)) {
  femaleHeroes.push(hero);
 }
}

let superhumans = [];
for (let hero of heroes) {
 if (isSuperhuman(hero)) {
  superhumans.push(hero);
 }
}

這種只返回 true 或者 false 的函數(shù),我們一般把它稱作斷言(predicate)函數(shù)。這里用了斷言(predicate)函數(shù)來判斷是否需要保留當前的英雄。

上面代碼的寫法會看起來比較長,但是把斷言函數(shù)抽離出來,可以讓重復(fù)的循環(huán)代碼更加明顯?,F(xiàn)在把種循環(huán)抽離到一個函數(shù)當中。

function filter(predicate, arr) {
 let working = [];
 for (let item of arr) {
  if (predicate(item)) {
   working = working.concat(item);
  }
 }
}

const femaleHeroes = filter(isFemaleHero, heroes);
const superhumans = filter(isSuperhuman, heroes);

同 map 和 reduce 一樣,JavaScript 提供了一個內(nèi)置數(shù)組方法,沒必要自己來實現(xiàn)(除非你自己想寫)。用內(nèi)置數(shù)組方法,上面的代碼就變成了:

const femaleHeroes = heroes.filter(isFemaleHero);
const superhumans = heroes.filter(isSuperhuman);

為什么這段代碼比 for...of 循環(huán)好呢?回想一下整個過程,我們要解決一個“找到滿足某一條件的所有英雄”。使用 filter 使得問題變得簡單化了。我們需要做的就是通過寫一個簡單函數(shù)來告訴 filter 哪一個數(shù)組元素要保留。不需要考慮數(shù)組是什么樣的,以及繁瑣的中間變量。取而代之的是一個簡單的斷言函數(shù),僅此而已。

與其他的迭代函數(shù)相比,使用 filter 是一個四兩撥千斤的過程。我們不需要通讀循環(huán)代碼來理解到底要過濾什么,要過濾的東西就在傳遞給它的那個函數(shù)里面。

finding

filter 已經(jīng)信手拈來了吧。這時如果只想找一個英雄該怎么辦?比如找 “Black Widow”。使用 filter 會這樣寫:

function isBlackWidow(hero) {
 return (hero.name === 'Black Widow');
}

const blackWidow = heroes.filter(isBlackWidow)[0];

這段代碼的問題是效率不夠高。filter 會檢查數(shù)組中的每一個元素,而我們知道這里面只有一個 “Black Widow”,當找到她的時候就可以停住,不用再看后面的元素了。那么,依舊利用斷言函數(shù),我們寫一個 find 函數(shù)來返回第一次匹配上的元素。

function find(predicate, arr) {
 for (let item of arr) {
  if (predicate(item)) {
   return item;
  }
 }
}

const blackWidow = find(isBlackWidow, heroes);

同樣地,JavaScript 已經(jīng)提供了這樣的方法:

const blackWidow = heroes.find(isBlackWidow);

find 再次體現(xiàn)了四兩撥千斤的特點。通過 find 方法,把問題簡化為:你只要關(guān)注如何判斷你要找的東西就可以了,不必關(guān)心迭代到底怎么實現(xiàn)等細節(jié)問題。

關(guān)于“JavaScript中map、reduce、filter和find的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。


新聞名稱:JavaScript中map、reduce、filter和find的示例分析
標題URL:http://weahome.cn/article/gohgcc.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部