Я хотел бы спросить, почему 0 часть аргумента?

Я понимаю всю логику метода reduce и параметр rest, но на самом деле не понимаю важности 0. Заранее большое спасибо!

      const sum = (...args) => {
   return args.reduce((a,b) => a + b, 0);
}

1 ответ

ECMAScript — это динамически типизированный/нетипизированный/однотипный язык программирования (в зависимости от того, кого вы спросите). Тем не менее, имеет смысл рассмотреть типыздесь.

Я буду использовать синтаксис TypeScript, на самом деле я буду использовать определение из библиотеки TypeScript , но фактический синтаксис не имеет большого значения:

      reduce<U>(
    callbackfn: (
        previousValue: U,
        currentValue: T,
        currentIndex: number,
        array: readonly T[]
    ) => U,
    initialValue: U
): U;

Тот факт, что исходный массив и текущий индекс передаются в функцию обратного вызова, является странностью ECMAScript, которая на самом деле не нужна для понимания того, как работает, поэтому давайте избавимся от них:

      reduce<U>(
    callbackfn: (
        previousValue: U,
        currentValue: T,
    ) => U,
    initialValue: U
): U;

Обратите также внимание, что здесь у нас есть два параметра разных типов:

  • является типом элемента .
  • является типом результата .

Итак, это функция, которая принимает три аргумента:

  • Невидимый this, который повторяется.
  • Функция , которая объединяет a и a и создает новый .
  • Начальное значение типа U.

Концептуально , что делает, так это то, что он «сводит» набор значений к одному значению. Важно понимать, что это единственное значение результата может иметь произвольный тип . Он не обязательно должен быть того же типа, что и тип элемента Array.

делает это, используя переданную ему функцию редукции.

Итак, концептуально то, что возвращает

      callbackfn(
    callbackfn(
        callbackfn(
            callbackfn(
                callbackfn(
                    callbackfn(
                        callbackfn(
                            callbackfn(
                                initialValue,
                                this[0]
                            ),
                            this[1]
                        ),
                        this[2]
                    ),
                    this[3]
                ),
                this[4]
            ),
            this[5]
        ),
        this[6]
    ),
    this[7]
)
// and so on …

Или, если мы напишем функцию обратного вызова как бинарный оператор ω:

      initialValue ω this[0] ω this[1] ω this[2] ω this[3] ω …

Обратите внимание, что функция обратного вызова должна быть левоассоциативной, т.е. эквивалентной

      (((((initialValue ω this[0]) ω this[1]) ω this[2]) ω this[3]) ω …)

Многие библиотеки коллекций, и ECMAScript не является исключением, также предоставляют версию, не имеющую начального значения. Во многих библиотеках эта версия имеет другое название (например, в Scala версия с начальным значением называется foldLeft, версия без называется reduceLeft, в Haskell версия с начальным значением называется foldl, версия без называется foldl1). В ECMAScript вместо этого используется "перегрузка", т.е. вы можете просто пропустить initialValueаргумент.

Если начальное значение опущено, вместо этого мы начинаем с первого элемента коллекции, т.е.

      callbackfn(
    callbackfn(
        callbackfn(
            callbackfn(
                callbackfn(
                    callbackfn(
                        callbackfn(
                            this[0],
                            this[1]
                        ),
                        this[2]
                    ),
                    this[3]
                ),
                this[4]
            ),
            this[5]
        ),
        this[6]
    ),
    this[7]
)
// and so on …

или записывается как оператор

      this[0] ω this[1] ω this[2] ω this[3] ω …

Однако это имеет важное последствие! Наш оператор/функция обратного вызова теперь должен принимать два s и возвращать a, больше нет способа получить тип результата, отличный от типа элемента.

И это имеет еще одно важное последствие: больше не работает с пустой коллекцией.

Таким образом, это две причины, по которым вам нужно указать начальное значение:

  • Вы хотите преобразовать тип.
  • Вы хотите работать с пустой коллекцией.

В данном конкретном случае первая причина неприменима: функция явно предназначена для уменьшения Array<number>к number. Но важна вторая причина: вы не хотите заставлять клиентов функции проверять, пуста ли коллекция. Клиент должен иметь возможность позвонить

      sum(...someArray)

и не волнуйся ли someArrayпусто или нет.

Возможность изменить тип на произвольный важна для универсальности . Оказывается, это мощная функция: она может делать все то же, что и вы, перебирая коллекцию. Другими словами: вы можете удалить все методыArray.prototypeкроме , а также удалить for … ofа также for … inиз ECMAScript, и вы по-прежнему можете делать все, что возможно, перебирая коллекцию. (Ну, я думаю, вам понадобится способ добавить что-то в массив, так что давайте продолжим pushтакже.)

Вот только пример реализации Array.prototype.mapс использованием Array.prototype.reduce:

      Array.prototype.map = function map(callbackFn) {
    return this.reduce(
        (previousValue, currentValue) =>
            previousValue.push(callbackFn(currentValue)),
        []
    );
}

Здесь вы видите, что тот факт, что мы можем изменить тип, важен, потому что мы начинаем с Array<T>, но мы не хотим Tв результате мы хотим Array<U>. Это также показывает, что утверждение « reduceсводит коллекцию к одному значению» не означает, что результатом должно быть простое значение. Значение результата может быть сколь угодно сложным… в данном случае это другой массив.

Другие вопросы по тегам