Почему у параметра Array.prototype.reduce нет параметра thisObject?

Методы Javascript Array, такие как forEach, имеют параметр thisArg, который используется как контекст для вызова обратного вызова:

array.forEach(callback[, thisArg])

как и every, some, filter и map. Однако reduce и reduceRight не имеют такого параметра. Есть ли какая-то особая причина для этого, или почему-то это не нужно?

Например, рассмотрим следующую реализацию функционального состава, используя reduceRight:

function compose () {
    var fns = [].slice.call(arguments,0);
    return function result() {
        return fns.reduceRight(
            function (prev,cur){
                return [cur.apply(this,prev)];
            },
            arguments
        )[0];
    };
}

Я хотел бы сделать это "this-aware", поэтому выполняемые функции вызывают в контексте, в котором вызывается функция, возвращаемая compose. В настоящее время они, похоже, вызывается в контексте глобального объекта. Я мог бы сделать старый var self=this; в верхней части функции result и использовать это как первый аргумент для вызова cur.apply, где у меня в настоящее время есть this, но это было бы необязательно, если reduce взял thisArg.

Мне что-то не хватает, и есть ли что-то о reduce, что делает это ненужным или непонятным?

UPDATE

@kangax Да, это произошло со мной. Далеко от меня критиковать дизайн API, но подпись для reduce кажется мне немного странной. Второй необязательный аргумент функционирует иначе, чем обычные необязательные аргументы, которые обычно имеют значение по умолчанию; вместо этого его присутствие или отсутствие изменяет поведение, существенно перегружая функцию, основанную на сигнатуре (счет аргумента). Когда второй параметр отсутствует, первый элемент массива становится стартовым, а первый вызов обратного вызова относится к второму значению. Мне кажется, что это поведение можно легко эмулировать, просто называя

array.slice(1).reduce(fn,array[0])

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

Ответы

Ответ 1

Он становится беспорядочным с двумя необязательными аргументами, поскольку reduce (Right) уже охватывает две функции (см. Wikipedia), которые различаются в чистых языках (например, с именем foldl и foldl1 в Haskell). Чтобы привести Brendan Eich:

Таким образом, это означает, что сокращение принимает один аргумент обратного вызова и два необязательных аргумента: thisObject и init. Какой из них должен быть первым? Наиболее распространенным является, вероятно, init, но тогда вы отделяете аргумент callback от arg этого "объекта", что, возможно, все в порядке. Несколько необязательных аргументов кажутся беспорядочными...

В качестве альтернативы мы могли бы просто исключить этот дополнительный аргумент "thisObject", так как люди всегда могут [использовать привязку].

Я не думаю, что это большая проблема, так как эти функциональные функции более высокого порядка в основном используются с функциональными выражениями lamdba - в любом случае (как в вашем примере). Конечно, там немного непоследовательности, но мы можем жить с этим. Выберите альтернативу:

 array.reduce(callback[, initialValue[, thisArg]])

Невозможно использовать, мы не можем определить ", если было предоставлено начальное значение VALUE" поскольку это означает arguments.length < 2 - мы могли бы пройти undefined буквально. Таким образом, это означает

 array.reduce(callback[, thisArg[, initialValue]])

что является уродливым, так как нам всегда нужно передать null или что-то в thisArg, если мы хотим только начальное значение.

Вы заметили, что уже в своем комментарии к Kangax ( "Второй необязательный аргумент функционирует иначе, чем обычные необязательные аргументы, [...] его присутствие или отсутствие изменяет поведение" ), но я не могу поддерживать ваше утверждение

это поведение можно легко эмулировать простым вызовом array.slice(1).reduce(fn,array[0])

так как он не будет работать с сложным (цепным) выражением вместо переменной array и b) громоздким.

Ответ 2

В списке рассылки Es-discuss вы найдете в ответ http://article.gmane.org/gmane.comp.lang.javascript.ecmascript4.general/4770

Другие методы с обратными вызовами принимают "thisArg" не потому, что это нужны или даже полезны, но для совместимости, потому что они уже в существующих реализациях, которые предоставляют эти функции.

Ответ 3

Вы всегда можете использовать этот метод:

array.reduce(callback.bind(context), initialValue);

..., чтобы связать конкретный контекст с функцией обратного вызова.