数组reduce方法详解

    |     2022年4月23日   |   html/css/js, web前端技术   |     0 条评论   |    1008

Array.prototype.reduce 按顺序对每个元素跑你提供的回调,每一次都把「到目前为止的汇总结果」传进去,最后收成一个返回值。求和、摊平、计数、去重,都可以写成一次 reduce。

签名是 reduce(callback, initValue?)。回调参数依次为 previousValue、currentValue、currentIndex、arr。initValue 可选,决定第一轮的起点。

一、有没有初始值

是否传入 initValue previousValue 起点 currentValue 起点 currentIndex 起点
有 就是 initValue arr[0] 0
无 arr[0] arr[1] 1

空数组且不给初始值会抛 TypeError。有初始值时,空数组直接返回该初始值。

二、先看遍历顺序

有初始值 0 时,第一轮 previous 是 0,current 从 4 起:

const test1 = () => {
	let arr = [4, 6, 7, 9]
	arr.reduce((previousValue, currentValue, currentIndex) => {
		console.log(`previousValue:${previousValue}, currentValue:${currentValue}, currentIndex:${currentIndex}`)
		return currentValue
	}, 0)
}

不给初始值时,第一轮 previous 直接是 4,current 从 6 起:

const test2 = () => {
	let arr = [4, 6, 7, 9]
	arr.reduce((previousValue, currentValue, currentIndex) => {
		console.log(`previousValue:${previousValue}, currentValue:${currentValue}, currentIndex:${currentIndex}`)
		return currentValue
	})
}

三、四个典型用法

累加

const test3 = () => {
	let arr = [4, 6, 7, 9]
	let sum = arr.reduce((previousValue, currentValue, currentIndex) => {
		console.log(previousValue) // 0,4,10,17
		return previousValue + currentValue // 4,10,17,26
	}, 0)
	console.log(sum)
}

对象数组同样可以,把 score 加到累加器上:

const test4 = () => {
	let arr = [
		{ name: 'jack', score: 98 },
		{ name: 'rose', score: 88 },
		{ name: 'tom', score: 90 },
	]
	let total = arr.reduce((previousValue, currentValue) => currentValue.score + previousValue, 0)
	console.log(total)
}

二维摊成一维

const test5 = () => {
	const arr = [
		[1, 2],
		[3, 4],
		[5, 6],
	]
	const newArr = arr.reduce((previousValue, currentValue) => [...previousValue, ...currentValue], [])
	console.log(newArr)
}

统计出现次数

把字符串拆成字符数组,累加器是对象。用 in 判断键是否已存在:

const test6 = () => {
	const str = 'helloworldjavascript'
	const arr = str.split('')
	const obj = arr.reduce(
		(previousValue, currentValue) => {
			currentValue in previousValue ? previousValue[currentValue]++ : (previousValue[currentValue] = 1)
			return previousValue
		}, {})
	console.log(obj)
}

去重

const test7 = () => {
	const arr = [1, 2, 3, 1, 2, 4, 5, 3]
	const newArr = arr.reduce((previousValue, currentValue) => {
		if (previousValue.indexOf(currentValue) == -1) {
			previousValue.push(currentValue)
		}
		return previousValue
	}, [])
	console.log(newArr)
}

四、自己实现一个 reduce

先用 Object.prototype.toString.call 分清 Function / Array / 其它类型,再按「有无初始值」决定起点下标:

const test8 = () => {
	let callback = null // [object Null]
	let value = Object.prototype.toString.call(callback)
	console.log(value)
}
Array.prototype.myReduce = function (callback, init) {
	if (Object.prototype.toString.call(callback) !== '[object Function]') {
		throw new TypeError('callback is not Function')
	}
	if (Object.prototype.toString.call(this) !== '[object Array]') {
		throw new TypeError('not Array')
	}
	if (this.length === 0 && init === undefined) {
		throw new TypeError('Reduce of empty arr with no initial value')
	}
	let index = init === undefined ? 1 : 0
	let previousValue = init === undefined ? this[0] : init
	const len = this.length
	while (index < len) {
		const currentValue = this[index]
		previousValue = callback(previousValue, currentValue, index, this)
		index++
	}
	return previousValue
}

原文用 !callback && toString !== Function 判断,空函数对象会漏检;这里改成只认 [object Function]。空数组错误信息里的 inital 也改成了 initial。

一句话总结:reduce 就是带累加器的遍历:先定初始值,再让每一轮的返回值成为下一轮的 previousValue,最后得到一个结果。

转载请注明来源:数组reduce方法详解
本文链接地址:https://ai.zhousir.top/?p=3281
回复 取消