前言
第一眼看 Array.reduce
这个函数总感觉怪怪的,用法也得花几分种才弄懂,懂了之后也不知道应用场景是啥。最近写项目的时候才慢慢对这个函数有更多的理解,可以算是 Array 类型下最强大的函数之一了。
API 用法
API 的用法分有无初始值两种情况:
没有初始值
const array = [1, 2, 3] array.reduce((prev, curt) => { return prev + curt }) // 1 + 2 + 3 = 6 复制代码
有初始值
const array = [1, 2, 3] array.reduce((prev, curt) => { return prev + curt }, 99) // 99 + 1 + 2 + 3 = 105 复制代码
reduce
这个函数的主要功能是将多个东西合成一个东西。大家都做过小学奥数吧,就类似于这样
reduce 所提供的功能就是这个加号,至于这怎么个加法,是由你来决定的。加法的过程可以形象地理解成贪吃蛇一样
🐍 (prev) + 💩(curt) + 💩 (curt) + 💩(curt) = 🛫 (return value) 复制代码
已有的蛇身就是 prev 参数,要吃掉的豆子就是 curt,吃完豆子的状态就是回调函数的返回值,整个 reduce 函数返回值就是这条🐍死了之后的状态。
应用场景
reduce
这个函数最难的点是想不出有什么使用场景。下面就做个抛砖引玉:
那我们先来思考一个问题:上面的例子只展示了数字的加法嘛,而 JS 里有 7 种基本数据类型:number, string, object, symbol, null, boolean, undefined。如果这些类型相加会怎么样呢?
除了这些基本类型,object 里也有 Array,Function,Object,Promise 这些类型,将这些类型做加法是不是也可以作为很多应用场景呢?
还有另一个点就是,除了加法,我还可以做减法,甚至做 comparasion,max,min 等操作。
将上面这 3 点都用起来,不难发现单单一个 reduce
就可以有几十种玩法。下面就选几种比较典型的例子给大家一些灵感。
所有的代理(包括源码和测试代码)都放在这里:github.com/Haixiang612…
max
Python 有这样的语法:max([1, 2, 3] // => 3
,JS 是没有的,使用 reduce 就可以简单地实现上面的功能:
type TComparator<T> = (a: T, b: T) => boolean function max<T>(array: T[], largerThan?: TComparator<T>): T { return array.reduce((prev, curt) => { return largerThan(prev, curt) ? prev : curt }) } export default max 复制代码
用例
describe('max', () => { it('返回简单元素最大值', () => { const array = [1, 2, 3, 4, 5] const result = max<number>(array, (a, b) => a > b) expect(result).toEqual(5) }) it('返回复杂对象的最大值', () => { const array: TItem[] = [ {value: 1}, {value: 2}, {value: 3} ] const result = max<TItem>(array, (a, b) => a.value > b.value) expect(result).toEqual({value: 3}) }) }) 复制代码
findIndex
JS 有一个 Array.indexOf
的 API,但是对于像 [{id: 2}, {id: 3}]
这样的数据结构就不行了,我们一般希望传一个回调去找对应的对象的下标。使用 reduce 可以这么写:
type TEqualCallback<T> = (item: T) => boolean function findIndex<T>(array: T[], isEqual: TEqualCallback<T>) { return array.reduce((prev: number, curt, index) => { if (prev === -1 && isEqual(curt)) { return index } else { return prev } }, -1) } export default findIndex 复制代码
用例
describe('findIndex', () => { it('可以找到对应的下标', () => { const array: TItem[] = [ {id: 1, value: 1}, {id: 2, value: 2}, {id: 3, value: 3}, ] const result = findIndex<TItem>(array, (item) => item.id === 2) expect(result).toEqual(1) }) }) 复制代码
filter
使用 reduce
一样可以重新实现 Array 下的一些 API,比如 filter
:
type TOkCallback<T> = (item: T) => boolean function filter<T>(array: T[], isOk: TOkCallback<T>): T[] { return array.reduce((prev: T[], curt: T) => { if (isOk(curt)) { prev.push(curt) } return prev }, []) } export default filter 复制代码
用例
describe('filter', () => { it('可以过滤', () => { const array: TItem[] = [{id: 1}, {id: 2}, {id: 3}] const result = filter<TItem>(array, (item => item.id !== 1)) expect(result).toEqual([{id: 2}, {id: 3}]) }) }) 复制代码