Логика работы метода reduce() в JavaScript
Метод reduce() обойдёт все элементы оригинального массива от начала и до конца. Метод двигается слева направо. Каждый вызов первого параметра метода (коллбэк), сокращает новый массив на один элемент — сокращает на первый элемент. То есть на каждом вызове метода будет уменьшаться длина нового массива. Возвращаемое значение первого параметра всегда будет первым элементом нового массива. И будет переназначаться каждый раз для каждого вызова коллбэка.
Пример работы
reduce() с первым параметром, а коллбэк с двумя параметрами
var m = ["aa","bb","cc","dd","ee","ff"] m.reduce((a,b)=>a+b) "aabbccddeeff"

Если приглядеться в итоговый результат, то это очень похоже на вызов метода join(«») с параметром в виде пустой строки. Каждый элемент массива склеился в хронологическом порядке. В результате образовалась одна длинная строка.
В этом примере произошла пошаговая конкатенация строк. Давайте посмотрим на вызовы коллбэка и то как менялся массив:
вызов 1 вернёт "aabb" ["aabb","cc","dd","ee","ff"] вызов 2 вернёт "aabbcc" ["aabbcc","dd","ee","ff"] вызов 3 вернёт "aabbccdd" ["aabbccdd","ee","ff"] вызов 4 вернёт "aabbccddee" ["aabbccddee","ff"] вызов 5 вернёт "aabbccddeeff" ["aabbccddeeff"]
У нас 6 элементов, но вызовов было 5, потому что сравниваются пары предыдущего значения и следующего — их будет 5.
В чём тут плюс? Мы можем очень быстро перевернуть результат, если это необходимо.
var m = ["aa","bb","cc","dd","ee","ff"] m.reduce((a,b)=>b+a) "ffeeddccbbaa"
Если бы использовали join(), то нужно было бы добавлять перед ним reverse()
[].concat(m).reverse().join("") "ffeeddccbbaa"

Если мы будем работать с массивом из чисел, то такая команда вернёт нам сумму всех значений элементов массива.
var m1 = [2,8,4,6,5,5] m1.reduce((a,b)=>b+a) или m1.reduce((a,b)=>a+b)
Результат работы

reduce() с первым параметром, а коллбэк с тремя параметрами
Третьим параметром коллбэка будет индекс массива.
var m = ["aa","bb","cc","dd","ee","ff"] m.reduce((a,b,c)=>c+a) "54321aa" m.reduce((a,b,c)=>c+b) "5ff" m.reduce((a,b,c)=>c+a+b) "54321aabbccddeeff" m.reduce((a,b,c)=>c+b+a) "5ff4ee3dd2cc1bbaa"
reduce() с первым параметром, а коллбэк с четырьмя параметрами
Четвёртым параметром коллбэка будет просматриваемый объект, по которому совершается обход.
var m = ["aa","bb","cc","dd","ee","ff"] m.reduce((a,b,c,e)=>c+a+e.length) "54321aa66666" или ["aa","bb","cc","dd","ee","ff"].reduce((a,b,c,e)=>c+a+e.length) "54321aa66666"
Мы задействовали сам массив и его свойство length, которое обозначает длину массива.
Можно использовать все четыре параметра в возвращаемом значении
m.reduce((a,b,c,e)=>c+b+a+e.length) "5ff4ee3dd2cc1bbaa66666"
Array.prototype.reduce ( callbackfn [ , initialValue ] )
Примечание 1
callbackfn должен быть функцией, которая принимает четыре аргумента. reduce вызывает callbackfn (обратный вызов) как функцию один раз для каждого элемента после первого элемента, присутствующего в массиве, в порядке возрастания.
callbackfn вызывается с четырьмя аргументами: previousValue (значение из предыдущего вызова callbackfn), currentValue (значение текущего элемента), currentIndex и просматриваемый объект, по которому совершается обход. При первом вызове этого обратного вызова previousValue и currentValue могут быть одним из двух значений:
- Если в вызове для уменьшения reduce было указано initialValue, то previousValue будет равно initialValue, а currentValue будет равно первому значению в массиве.
- Если значение initialValue не было указано, то previousValue будет равно первому значению в массиве, а currentValue будет равно второму. Это ошибка TypeError, если массив не содержит элементов и не указано initialValue.
reduce не изменяет напрямую объект, для которого он вызывается, но объект может быть изменен вызовами callbackfn.
Диапазон элементов, обрабатываемых с помощью reduce, устанавливается перед первым вызовом callbackfn. Элементы, которые добавляются к массиву после начала вызова reduce, не будут посещаться callbackfn. Если существующие элементы массива изменены, их значение, переданное в callbackfn, будет значением на момент их посещения командой reduce; элементы, которые удаляются после начала вызова reduce и до посещения, не посещаются.
Когда метод reduce вызывается с одним или двумя аргументами, выполняются следующие шаги:
1. Пусть O будет ? ToObject(значение this) 2. Пусть len будет ? LengthOfArrayLike(O) 3. Если IsCallable(callbackfn) имеет значение false, генерировать исключение TypeError. 4. Если len = 0 и initialValue отсутствует, выбросить исключение TypeError. 5. Пусть k равно 0. 6. Пусть accumulator не определен - undefined. 7. Если присутствует initialValue, то a. Установите accumulator на initialValue. 8. Иначе, a. Пусть kPresent ложно false. b. Повторите, пока kPresent имеет значение false и k < len, i. Пусть Pk будет ! ToString(𝔽(k)). ii. Установите для kPresent значение ? HasProperty(O, Pk). iii. Если kPresent истинно true, то 1. Установить accumulator на ? Get(O, Pk). iv. Установите k на k + 1. c. Если kPresent имеет значение false, выбросить исключение TypeError. 9. Повторить, пока k < len, a. Пусть Pk будет ! ToString(𝔽(k)). b. Пусть kPresent будет ? HasProperty(O, Pk). c. Если kPresent истинно true, то i. Пусть kValue будет ? Get(O, Pk). ii. Установить accumulator на ? Call(callbackfn, undefined, «accumulator, kValue, 𝔽(k), O»). d. Установите k на k + 1. 10. Вернуть accumulator
Примечание 2
Функция reduce намеренно универсальна; она не требует, чтобы значение this было объектом массива. Поэтому его можно передать другим видам объектов для использования в качестве метода.
Информационные ссылки
Стандарт ECMAScript — Раздел «23.1.3.21 Array.prototype.reduce ( callbackfn [ , initialValue ] )» — https://tc39.es/ecma262/#sec-array.prototype.reduce