
少俠們好~
今天和大家分享一篇關(guān)于JS數(shù)組里面的map,filter,reduce相關(guān)函數(shù)的知識,
相信少俠們肯定或多或少都了解過一些相關(guān)知識,特別對于map和filter這種很常用的函數(shù)的用法,可能已經(jīng)信手拈來了,
但是~
少俠,
你真的確定,你目前所使用的方式,就是它們的正確使用姿勢嗎?
如果想知道所作的選擇是否明智,就看你在清楚知道將會付出什么代價的情況下,是否依然選擇那樣做。
————《黑客帝國3》
首先,讓我們從最一個比較簡單的例子開始:
這里有一個從1到10的數(shù)組:

現(xiàn)在,如果少俠你需要取出中間的奇數(shù),你會怎么做呢?
這個問題不難,有的少俠可能會使用for循環(huán)來過濾出偶數(shù),某些少俠可能會使用while循環(huán),又或是使用數(shù)組自帶的filter方法。
這里,我們先不考慮使用for和while這些迭代方法,我們選擇使用數(shù)組自帶的filter方法,如果使用filter方法的話,
少俠你的代碼大概可能會是這樣的:

還是挺簡單的對吧?
相信大部分少俠都能很輕松完成。
在filter函數(shù)內(nèi)部,我們使用 num % 2 !== 0 來判斷 是否是一個奇數(shù),如果是一個奇數(shù),就過濾出來。
在這里,有的少俠可能就會想到,如果把這個判斷過程單獨(dú)分成一個函數(shù),會方便一些,也方便以后的修改擴(kuò)展。
比如,單獨(dú)寫成一個 isOdd函數(shù):

然后在filter里面使用這個函數(shù),是吧?
但是!
一些少俠這里就會開始犯一個天真的錯誤了,
你可能會寫成下面這樣:

看見這樣的代碼,有的少俠可能會說,天辰,這樣寫沒有什么問題啊,我也測試過了,結(jié)果都是正確的,怎么就天真了?
沒錯,結(jié)果是正確的,
但是,你每次在filter里面都額外調(diào)用了一次并不一定需要的函數(shù)。
也就是包裹著 return isOdd(num)的那個外層箭頭函數(shù)。

如果你還沒看出來問題的話,先看看這個例子:

這里的sayHello接受一個字符串name, 然后返回 'hello' + name.
helpSayHello同樣接受一個字符串name,然后用這個name 調(diào)用 sayHello, 得到返回的 'hello' + name.
那么,少俠你看看,下面兩句代碼結(jié)果有什么區(qū)別呢?
我們干嘛不直接調(diào)用sayHello呢?

“這樣啊,大概理解了,但是天辰你告訴我,鴿子為什么那么大? 到底哪里調(diào)用了2次了?”
(不知道鴿子為什么那么大什么意思的點(diǎn)擊這里查看~)

“。。。。。。”
好吧,那換個角度,

現(xiàn)在發(fā)現(xiàn)了沒?
filter里面那個函數(shù)是不是和isOdd一模一樣?
所以,我們直接把isOdd放進(jìn)去就可以了:

“道理我懂了,但是誰能告訴我,鴿子到底為什么那么大? 為什么之前那樣會多調(diào)用一次函數(shù)?”
“。。。。。。。算了,接著看下面的內(nèi)容吧”
如果要把nums里面的數(shù)字翻倍,用map函數(shù),又該怎么做呢?
聰明的少俠這次肯定一下就明白了:

好了,既然現(xiàn)在我們知道了如何從nums數(shù)組里面過濾出奇數(shù),如何翻倍里面的數(shù)字,那么,接下來我們就更進(jìn)一步:
從nums里面過濾出所有奇數(shù),并翻倍這些奇數(shù)。
這個問題也不難是吧?
讓我猜一下,一些少俠應(yīng)該會寫成下面這樣:

如果我們還想繼續(xù)算出所有奇數(shù)翻倍后的和的話:

嗯,一個串一個看起來很酷是吧? 最后的結(jié)果也是正確的。
但是。。。
少俠你依然比較天真!
這樣串起來的后果就是,我們會遍歷3次數(shù)組,filter的時候遍歷一次,map的時候遍歷一次,reduce的時候再遍歷一次。
現(xiàn)在數(shù)組只有10個數(shù)字,影響還不大,但是如果是很大的數(shù)組,比如100萬個數(shù)字,遍歷3遍的話,那么就會多訪問150萬次,
150萬次什么概念?
少俠你有150萬存款嘛?
更壞的情況會是當(dāng)你習(xí)慣這種方式后,你可能會連接更多的函數(shù)。。。。
array
.map(fn1)
.map(fn2)
.filter(fn4)
.filter(fn5)
.forEach(fn6)
.....
所以,這種方式其實(shí)不算是一種很好的方式,
到這里有的少俠可能要問了,如果這種方式不是很好,有什么更好的方式呢? 我要怎么去找到更好的方式呢?
實(shí)際上,
要找到更好的方式,這里我們首先可以嘗試的是
憑感覺。。。。
想象一下,如果讓你自己從中間取出所有奇數(shù),翻倍,并求和,你會覺得怎么做簡單一些呢?
是不是覺得下面這樣才比較正常?
首先,從第一個數(shù)字開始,如果它是奇數(shù),就拿出來,翻倍,然后找個地方放起來,
接著,
繼續(xù)看第二個數(shù)字,如果是偶數(shù),就跳過,如果是奇數(shù),就拿出來,翻倍,然后和開始的翻倍后的奇數(shù)加起來,找個地方放起來。
接著看第三個數(shù)字,以此類推,只要是奇數(shù),就翻倍,然后和之前的結(jié)果加起來。
直到我們找尋到最后一個數(shù)字,就可以算出最終結(jié)果了,
對吧?我們并不需要再回頭看了。
所以,這樣才是正確的方式。
如果使用迭代的話,就是下面這樣:

沒錯,通常這樣才是正常的操作。
但是,
這樣的代碼不太方便,
少俠你肯定也不想每次都寫這么一大堆for循環(huán)語句吧?
所以,我們可以先試著繼續(xù)優(yōu)化一下。
我們首先試著用一個叫做magic(魔法)的函數(shù)把這些步驟包裹起來:

現(xiàn)在看起來好多了,不過,magic內(nèi)部的操作看起來不是很清晰,
其他少俠看見了可能很難一眼就明白magic函數(shù)在做什么,
我們可以試著把里面的一些步驟分成更小的函數(shù):

去掉注釋,換個稍微簡潔點(diǎn)的寫法:

現(xiàn)在比較簡潔也比較清晰了,不過,假如我們現(xiàn)在要計算是偶數(shù)而不是奇數(shù)怎么辦呢?
或者如果更復(fù)雜些,如果是偶數(shù),就反過來,除以二,然后求和,又該怎么辦呢?
難道我們又要復(fù)制一次magic函數(shù),然后改動for循環(huán)里面的邏輯嗎?
也許,我們可以把for循環(huán)內(nèi)部的邏輯單獨(dú)提取成一個外部函數(shù)!

但是,
少俠們注意了!
這里的問題是,在內(nèi)部的時候,for可以通過閉包規(guī)則,訪問到array, 訪問到array[i],以及sum,
而當(dāng)我們把magicFriend函數(shù)放在外面之后,它沒辦法再訪問到magic內(nèi)部的array, array[i],以及sum了,
因?yàn)橹八羞壿嫸荚趍agic函數(shù)內(nèi)部,可以通過閉包規(guī)則訪問到所需變量,現(xiàn)在不行了。
那么怎么解決呢?
很簡單~
這里我們真正關(guān)心的是,如何通過其他方式獲取到之前通過閉包獲取到的參數(shù)。
既然閉包的規(guī)則沒辦法幫助我們獲取到所需參數(shù),我們就換一個能幫我們獲取到對應(yīng)參數(shù)的規(guī)則,這里我們就換成通過函數(shù)參數(shù)來獲取:

當(dāng)然,現(xiàn)在在magic內(nèi)部,for循環(huán)里面嚴(yán)重依賴當(dāng)前作用域中一個叫做magicFriend的函數(shù),如果當(dāng)前作用域中的magicFriend函數(shù)不見了,magic函數(shù)就沒辦法正常運(yùn)行了,
為了更靈活,我們可以把magicFriend也換成通過參數(shù)傳遞給magic。

等一等!
還有個問題。。。
萬一我們希望sum從100開始相加呢?
算了,那就把sum也提取出來,通過參數(shù)傳進(jìn)去吧。。。

到這里了,有些少俠可能要問了!
天辰你到底有完沒完了?
整這些花里胡哨的有什么用?
別急! 少俠~ 接下來就是見證奇跡的時刻!
首先,我們給代碼里面部分函數(shù)和參數(shù)改一下名字:

是不是有點(diǎn)眼熟了? 接著,我們換成數(shù)組自帶的reduce函數(shù):

最后回到我們上面的問題:

完全OK!
這次我們用reduce只遍歷了一次數(shù)組就完成了之前的操作!
現(xiàn)在知道為什么之前要把reduce叫做magic(魔法)了吧!
不過~~
到這里, 有的少俠可能會覺得有點(diǎn)奇怪,那么之前的filter和map呢? 為什么現(xiàn)在就不需要它們了呢?
針對這個問題,天辰我的答案是:
因?yàn)槲覀兏揪筒皇窃趩渭兊貓?zhí)行map或filter操作~
如果少俠你真的只是單純地想把一堆數(shù)據(jù),映射成另一對數(shù)據(jù),才是用map。
如果少俠你真的只是單純地想過濾出一些數(shù)據(jù),才是用filter。
除此之外,如果你既要過濾(filter)一些東西,同時又要映射(map)一些東西,
那么,少俠你實(shí)際上既不是在做map操作,也不是做filter操作,這個時候,請考慮采用reduce(magic)!
不行! 說好了只使用map,多一個filter,多一個map,都不算map!

好了,
恭喜你,少俠!
你成功發(fā)現(xiàn)并閱讀完了這篇文章~
首先~
謝謝少俠你看到了這里,
然后~
不管少俠你現(xiàn)在處于什么階段,希望你都能從中有所收獲。
那么現(xiàn)在要結(jié)束了?
當(dāng)然不行!
不管你們下次還有沒有興趣,按照慣例,在結(jié)束之前必須留個懸念吊一下胃口~
所以,提前預(yù)告一下下期內(nèi)容。。。
下一次,天辰會和少俠們分享一個有趣的武器,
這個武器是天辰費(fèi)了很大勁從救贖(JS)大陸獲取到的,能夠更酷(更裝逼)地處理我們今天遇到的問題,
雖然可能需要少俠你有一定的修為才能使用,
但是~
不管怎樣,我相信少俠你最終肯定能掌握它。

一些你可能關(guān)心的問題:
1、天辰,你說的這些,能幫助我漲工資嗎?
說能的話顯得有點(diǎn)裝逼!但是,少俠如果你能像這樣堅持不斷學(xué)習(xí)的話,相信你以后不僅能漲工資,還能給別人發(fā)工資!
2、天辰,我不關(guān)心工資,我關(guān)心的是,了解這些能幫助我找到女朋友嗎?
看見這個狗頭了沒??
我要有找女朋友的方法我還會在這寫技術(shù)文章?!
當(dāng)然,不排除某些優(yōu)秀的少俠能在開發(fā)妹子面前用技術(shù)裝逼從而吸引到妹子。。。
3、還有!天辰你的代碼為什么要放圖片,而不是直接貼文字代碼呢?我都不能復(fù)制粘貼測試。
第一,放圖片應(yīng)該要好看些。
第二,不能復(fù)制代碼就對了!少俠你應(yīng)該自己動手敲一遍代碼,這樣印象才比較深刻,請養(yǎng)成自己動手的好習(xí)慣!
好了,
少俠,江湖路上,有緣再見~
