Давайте рассмотрим код на очень простом примере:
compose(f2, f3, f4)
Поскольку не было передано начальное значение для уменьшения, оно начнется с первого (f2) и второго (f3) значениямассив и вызов обратного вызова с этим, x
вызывается с a
, являющимся f2
и b
, являющимся f3
.Теперь x
ничего не делает, просто возвращает функцию y
, которая может получить доступ к a и b через замыкание.
Теперь уменьшение будет продолжено до третьего элемента, первый аргумент будет результатом предыдущего обратного вызова (закрытый y
), а второй аргумент будет f4
.Теперь x
вызывается снова, и для y
создается другое замыкание, y
возвращает окончательно возвращенную функцию целиком.
Если мы попытаемся визуализировать таким образом закрытую функцию, это будет:
y { // closure of y
a -> y { // a references another closure of y
a -> f3,
b -> f2
},
b -> f4
}
Теперь вы вызываете закрытое y
и передаете в него 2, что вызовет b
(f4
) и передают результат в вызов a
(закрытое y).
a ( b(...args))
y { ... } ( f4(2) )
Теперь, когда замыкание y будет делать то же самое:
a ( b ( ...args))
f2( f3( f4( 2 ) ) )
Подсказка: Иногда очень трудно отслеживать закрытые значения, поэтомуконсоль предоставляет вам отличные утилиты для их отслеживания: откройте ваш код на вкладке «отладчик» консоли, щелкните по номерам строк, где вызовы функций предназначены для присоединения точек останова, затем снова запустите код, выполнение будет выполняться всякий раз, когдаТочка останова достигнута, и вы можете видеть значения всех переменных (включая закрытые).