Array.prototype.reduce 的理解与实现
Array.prototype.reduce 是 JavaScript 中比較實用的一個函數(shù),但是很多人都沒有使用過它,因為 reduce 能做的事情其實 forEach 或者 map 函數(shù)也能做,而且比 reduce 好理解。但是 reduce 函數(shù)還是值得去了解的。
reduce 函數(shù)可以對一個數(shù)組進(jìn)行遍歷,然后返回一個累計值,它使用起來比較靈活,下面了解一下它的用法。
reduce 接受兩個參數(shù),第二個參數(shù)可選:
@param {Function} callback 迭代數(shù)組時,求累計值的回調(diào)函數(shù) @param {Any} initVal 初始值,可選其中,callback 函數(shù)可以接受四個參數(shù):
@param {Any} acc 累計值 @param {Any} val 當(dāng)前遍歷的值 @param {Number} key 當(dāng)前遍歷值的索引 @param {Array} arr 當(dāng)前遍歷的數(shù)組callback 接受這四個參數(shù),經(jīng)過處理后返回新的累計值,而這個累計值會作為新的 acc 傳遞給下一個 callback 處理。直到處理完所有的數(shù)組項。得到一個最終的累計值。
reduce 接受的第二個參數(shù)是一個初始值,它是可選的。如果我們傳遞了初始值,那么它會作為 acc 傳遞給第一個 callback,此時 callback 的第二個參數(shù) val 是數(shù)組的第一項;如果我們沒有傳遞初始值給 reduce,那么數(shù)組的第一項會作為累計值傳遞給 callback,數(shù)組的第二項會作為當(dāng)前項傳遞給 callback。
示例:
對數(shù)組求和:
let arr = [1, 2, 3]; let res = arr.reduce((acc, v) => acc + v); console.log(res); // 6如果我們傳遞一個初始值:
let arr = [1, 2, 3]; let res = arr.reduce((acc, v) => acc + v, 94); console.log(res); // 100利用 reduce 求和比 forEach 更加簡單,代碼也更加優(yōu)雅,只需要清楚 callback 接受哪些參數(shù),代表什么含義就可以了。
我們還可以利用 reduce 做一些其他的事情,比如對數(shù)組去重:
let arr = [1, 1, 1, 2, 3, 3, 4, 3, 2, 4]; let res = arr.reduce((acc, v) => {if (acc.indexOf(v) < 0) acc.push(v);return acc; }, []); console.log(res); // [1, 2, 3, 4]統(tǒng)計數(shù)組中每一項出現(xiàn)的次數(shù):
let arr = ['Jerry', 'Tom', 'Jerry', 'Cat', 'Mouse', 'Mouse']; let res = arr.reduce((acc, v) => {if (acc[v] === void 0) acc[v] = 1;else acc[v]++;return acc; }, {}); console.log(res); // {Jerry: 2, Tom: 1, Cat: 1, Mouse: 2}將二維數(shù)組展開成一維數(shù)組:
let arr = [[1, 2, 3], 3, 4, [3, 5]]; let res = arr.reduce((acc, v) => {if (v instanceof Array) {return [...acc, ...v];} else {return [...acc, v];} }); console.log(res); // [1, 2, 3, 3, 4, 3, 5]由此可以看出,reduce 函數(shù)還是很實用的,但是 reduce 函數(shù)兼容性不是特別好,只支持到 IE 9,如果要在 IE 8 及以下使用的話就不行了,所以我們可以自己實現(xiàn)一下,還可以對其做一下擴(kuò)展,使其能夠遍歷對象。
首先可以實現(xiàn)一個最基礎(chǔ)的 each 函數(shù),作為我們 reduce 的基礎(chǔ):
/*** 遍歷對象或數(shù)組,對操作對象的屬性或元素做處理* @param {Object|Array} param 要遍歷的對象或數(shù)組* @param {Function} callback 回調(diào)函數(shù)*/ function each(param, callback) {// ...省略參數(shù)校驗if (param instanceof Array) {for (var i = 0; i < param.length; i++) {callback(param[i], i, param);}} else if (Object.prototype.toString.call(param) === '[object Object]') {for (var val in param) {callback(param[val], val, param);}} else {throw new TypeError('each 參數(shù)錯誤!');} }可以看出 each 可以遍歷對象或數(shù)組,回調(diào)函數(shù)接受三個參數(shù):
@param {Any} v 當(dāng)前遍歷項 @param {String|Number} k 當(dāng)前遍歷的索引或鍵 @param {Object|Array} o 當(dāng)前遍歷的對象或者數(shù)組有了這個基礎(chǔ)函數(shù),我們可以開始實現(xiàn)我們的 reduce 函數(shù)了:
/*** 迭代數(shù)組、類數(shù)組對象或?qū)ο?#xff0c;返回一個累計值* @param {Object|Array} param 要迭代的數(shù)組、類數(shù)組對象或?qū)ο? @param {Function} callback 對每一項進(jìn)行操作的回調(diào)函數(shù),接收四個參數(shù):acc 累加值、v 當(dāng)前項、k 當(dāng)前索引、o 當(dāng)前迭代對象* @param {Any} initVal 傳入的初始值*/ function reduce(param, callback, initVal) {var hasInitVal = initVal !== void 0;var acc = hasInitVal ? initVal : param[0];each(hasInitVal ? param : Array.prototype.slice.call(param, 1), function(v, k, o) {acc = callback(acc, v, k, o);});return acc; }可以看到,我們的 reduce 函數(shù)就是在 each 上面封裝了一層。根據(jù)是否傳遞了初始值 initVal 來決定遍歷的起始項。每次遍歷都接受 callback 返回的 acc 值,然后在 reduce 的最后返回 acc 累計值就可以啦!
當(dāng)然,這部分代碼有一個很嚴(yán)重的 bug,導(dǎo)致了我們的 polyfill 毫無意義,那就是遍歷對象時的 for...in。這個語法和在 IE <= 9 環(huán)境下存在 bug,會無法獲得對象的屬性值,這就導(dǎo)致我們所實現(xiàn)的 reduce 無法在 IE 9 以下遍歷對象,但是遍歷數(shù)組還是可以的。對于 for...in 的這個 bug,可以參考 underscore 是怎么實現(xiàn)的,這里暫時不研究了~
轉(zhuǎn)載于:https://www.cnblogs.com/DM428/p/10126885.html
總結(jié)
以上是生活随笔為你收集整理的Array.prototype.reduce 的理解与实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [react] create-react
- 下一篇: PostSql创建用户