比你想象中更強(qiáng)大的 reduce 以及對(duì)敲碼的思考

前言
前面的這篇文章 JS 基礎(chǔ)\! | 扁平數(shù)組和JSON樹(shù)的轉(zhuǎn)換[2] 利用到了 reduce來(lái)實(shí)現(xiàn)數(shù)組轉(zhuǎn)為map,以及結(jié)合concat實(shí)現(xiàn)數(shù)組遞歸拼接。今天我們來(lái)看看還能搞些什么名堂~

簡(jiǎn)單復(fù)習(xí)一下 reduce
語(yǔ)法
let value = arr.reduce(function(previousValue, item, index, array) {
  // ...
}, [initial]);

參數(shù):
previousValue: 上一個(gè)函數(shù)調(diào)用的結(jié)果,第一次等于 initial(如果提供了 initial 的話)。
item : 當(dāng)前的數(shù)組元素。
index :當(dāng)前索引。
arr : 數(shù)組本身。
previousValue實(shí)際上有點(diǎn)像累加,所以一些地方也會(huì)叫將這個(gè)參數(shù)稱(chēng)為accumulator ,存儲(chǔ)前面所有的執(zhí)行結(jié)果,最后會(huì)成為reduce的結(jié)果。

可讀性
但通常,你一般是不會(huì)完全參照這些參數(shù)定義的意思來(lái)決定參數(shù)名稱(chēng),而是要看具體開(kāi)發(fā)遇到的場(chǎng)景來(lái)給其取可讀性高的名稱(chēng)。

部分應(yīng)用
1. 經(jīng)典累加器
將數(shù)組中的值從左到右累加,大家學(xué)reduce的時(shí)候第一個(gè)例子應(yīng)該就是這個(gè)。這里只是簡(jiǎn)單的提一下,不是本文的重點(diǎn)~

const a = [1, 2, 8, 7, 4];

const sum = a.reduce((pre, cur) => {
 const res = pre + cur;
 return res;
}, 0);

console.log(sum); // 22

2. 二維數(shù)組轉(zhuǎn)一維
結(jié)合 concat 實(shí)現(xiàn)數(shù)組扁平化

const arr2 = [
 [1, 2],
 [3, 4],
 [5, 6],
].reduce((acc, cur) => {
 return acc.concat(cur);
}, []);

console.log(arr2); //

3. 多維數(shù)組扁平
這個(gè)算是上一個(gè)的進(jìn)階和更為通用的版本~
這篇文章[3]里就用到了類(lèi)似的,結(jié)合 concat 和遞歸

const arr3 = [
 [1, 2],
 [3, 4],
 [5, [7, [9, 10], 8], 6],
];
const flatten = arr =>
 arr.reduce(
  (pre, cur) => pre.concat(Array.isArray(cur) ? flatten(cur) : cur),
  []
 );
console.log(flatten(arr3)); // [ 1, 2, 3, 4, 5, 7, 9, 10, 8, 6 ]






4. 數(shù)組分塊
根據(jù)傳入限制大小,對(duì)數(shù)組進(jìn)行分塊。
小于限制長(zhǎng)度時(shí)就往里添加,否則直接將其加入res

const chunk = (arr, size) => {
 return arr.reduce(
  (res, cur) => (
   res[res.length - 1].length < size
    ? res[res.length - 1].push(cur)
    : res.push([cur]),
   res
  ),
  [[]]
 );
};
const arr4 = [1, 2, 3, 4, 5, 6];
console.log(chunk(arr4, 3)); // [ [ 1, 2, 3 ], [ 4, 5, 6 ] ]

5. 字符統(tǒng)計(jì)
統(tǒng)計(jì)文本中各個(gè)字的數(shù)量

const countChar = text => {
 text = text.split("");
 return text.reduce((record, c) => {
  record[c] = (record[c] || 0) + 1;
  return record;
 }, {});
};
const text = "劃水水摸魚(yú)魚(yú)";
console.log(countChar(text)); // { '劃': 1, '水': 2, '摸': 1, '魚(yú)': 2 }

思考
本文只講了幾個(gè)應(yīng)用,其實(shí)還有更多的應(yīng)用,以及一些實(shí)現(xiàn) JS API 的功能,比如

數(shù)組填充
求最大、最小值
reverse
map
...
等等,這些代碼段的整合網(wǎng)上已經(jīng)有夠多了,我這里想總結(jié)一下寫(xiě)出這些實(shí)用、通用的代碼需要進(jìn)行怎樣的思考

core-js
其實(shí)從 Polyfill ofArray.prototype.reduceincore-js[4] 中可以從另一個(gè)角度理解 reduce 中第一個(gè)參數(shù)回調(diào)函數(shù)中接收的第一個(gè)參數(shù):

class Array {
  //...
  reduce(callbackfn: (memo: any, value: any, index: number, target: any) => any, initialValue?: any): any;
 //...
}

他這里用的是 memo,中文直接翻譯過(guò)來(lái)就是備忘錄,在計(jì)算機(jī)中我們或許更為喜歡用緩存。我個(gè)人認(rèn)為是更為符合我們寫(xiě)出好用的代碼片段的。
他的精髓所在正是將回調(diào)函數(shù)作用于數(shù)組中的各個(gè)成員上,而上一次return的值就作為memo,傳入到下一個(gè)之中,你可以在這里面逐一處理:

不斷更新迭代(累加)
將結(jié)果拼接(扁平化)
將特定的值添加到同一個(gè)對(duì)象之中(字符統(tǒng)計(jì))
最后作為想要得到最終效果展現(xiàn)出來(lái)~

總結(jié)



寫(xiě)代碼,大概是這么幾個(gè)節(jié)點(diǎn)

了解語(yǔ)法
傳參
返回
真正領(lǐng)悟到語(yǔ)言的特性,并能將其運(yùn)用到實(shí)際開(kāi)發(fā)中,寫(xiě)出更好更簡(jiǎn)潔更實(shí)用的代碼
多看:要從從這些代碼段中以及網(wǎng)上各路好漢寫(xiě)的實(shí)用代碼學(xué)習(xí)怎么樣真正地像寫(xiě)詩(shī)一樣寫(xiě)代碼~
多寫(xiě)
第四步的多看,我感覺(jué)真的是收益匪淺,如果你有看源碼的學(xué)習(xí)習(xí)慣[5],真的會(huì)學(xué)到很多~ 例如其實(shí) Redux.compose中也用到了reduce。真的感覺(jué)有些代碼,如果我從沒(méi)有見(jiàn)到過(guò),我絕對(duì)寫(xiě)不出來(lái)...

關(guān)于本文

來(lái)自:前舟

https://juejin.cn/post/7102591881204203557


作者:前舟


歡迎關(guān)注微信公眾號(hào) :前端Q