Я предполагаю, что это зависит от того, если вы хотите forEach
и reduce
быть похожими (простой) или как можно ближе/разумно к ECMA5 спецификации (игнорируя ошибки браузера), мне нравится как можно ближе/разумным.
Array.prototype.forEach (callbackfn [ , thisArg ])
callbackfn должна быть функция, которая принимает три аргумента. forEach вызывает callbackfn один раз для каждого элемента, присутствующего в массиве, в порядке возрастания.callbackfn вызывается только для элементов существующего массива; он не вызывается для отсутствующих элементов массива.
Если указан параметр thisArg, он будет использоваться как это значение для каждого вызова callbackfn. Если он не указан, вместо него используется undefined.
callbackfn вызывается с тремя аргументами: значением элемента, индексом элемента и пройденным объектом.
forEach не напрямую мутирует объект, на который он вызывается, но объект может быть мутирован вызовами callbackfn.
Диапазон элементов, обработанных forEach, устанавливается перед первым вызовом callbackfn. Элементы, которые добавляются в массив после вызова метода forEach, не будут посещаться callbackfn. Если существующие элементы массива изменены, их значение, переданное обратному вызову, будет значением в то время, когда каждый посетит их; элементы, которые удаляются после вызова метода forEach и до его посещения, не посещаются.
Когда метод Foreach вызывается с одним или двумя аргументами выполняются следующие шаги:
- Пусть O будет результатом вызова ToObject передавая это значение в качестве аргумента.
- Пусть lenValue является результатом вызова внутреннего метода O [[Get]] с аргументом «length».
- Позвольте len быть ToUint32 (lenValue).
- Если IsCallable (callbackfn) является ложным, введите исключение TypeError.
- Если этотArg был поставлен, пусть T - этоArg; иначе пусть T не определено.
- Пусть к 0.
- Repeat, в то время как к < Len
- Пусть Рк ToString (к).
- Пусть kPresent является результатом вызова внутреннего метода O [[HasProperty]] с аргументом Pk.
- Если kPresent является истинным, то
- Пусть kValue будет результатом вызова внутреннего метода O [[Get]] с аргументом Pk.
- Вызвать метод [[Call]] внутренний метод callbackfn с Т в качестве списка этого значения аргумента и содержащего-значение, К и О.
- Увеличения к на 1.
- Возврат не определен.
Длина свойство метода Foreach равно 1.
Примечание Функция Foreach намеренно родовое; он не требует, чтобы это значение было объектом Array. Поэтому он может быть передан другим типам объектов для использования в качестве метода. Может ли функция forEach успешно применяться к объекту хоста, зависит от реализации.
-
Array.prototype.reduce (callbackfn [ , initialValue ])
callbackfn должна быть функция, которая принимает четыре аргумента.уменьшает вызовы callback, как функцию, один раз для каждого элемента, присутствующего в массиве, в порядке возрастания.
callbackfn вызывается с четырьмя аргументами: previousValue (или значением предыдущего вызова callbackfn), currentValue (значение текущего элемента), currentIndex и проходящий объект. При первом вызове обратного вызова значение previousValue и currentValue может быть одним из двух значений. Если в вызове было предложено initialValue, то значение предыдущегоValue будет равно initialValue, а currentValue будет равно первому значению в массиве. Если initialValue не было предоставлено, то предыдущее значение Value будет равно первому значению в массиве, а currentValue будет равно второму. Это TypeError, если массив не содержит элементов, а initialValue не предоставляется.
сокращение не напрямую мутирует объект, на который он вызывается, но объект может быть мутирован вызовами callbackfn.
Диапазон элементов, обработанных сокращением, устанавливается перед первым вызовом callbackfn. Элементы, которые добавляются к массиву после начала вызова для отказа, не будут посещаться callbackfn. Если существующие элементы массива изменены, их значение, переданное callbackfn, будет значением, которое в то время уменьшает количество посещений; элементы, которые удаляются после начала вызова до начала и перед посещением, не посещаются.
Когда метод снижения вызывается с одним или двумя аргументами выполняются следующие шаги:
- Пусть O будет результатом вызова ToObject передавая это значение в качестве аргумента.
- Пусть lenValue является результатом вызова внутреннего метода O [[Get]] с аргументом «length».
- Позвольте len быть ToUint32 (lenValue).
- Если IsCallable (callbackfn) является ложным, введите исключение TypeError.
- Если len равно 0, а initialValue нет, введите исключение TypeError.
- Пусть к 0.
- Если InitialValue присутствует, то
- Установите аккумулятор в InitialValue.
- Else, initialValue нет
- Пусть kPresent будет ложным.
- Повторите, в то время как kPresent является ложным и k < len
- Пусть Pk be ToString (k).
- Пусть kPresent является результатом вызова внутреннего метода O [[HasProperty]] с аргументом Pk.
- Если kPresent является истинным, то
- Позвольте аккумулятору быть результатом вызова внутреннего метода O [[Get]] с аргументом Pk.
- Увеличение k на 1.
- Если kPresent является ложным, введите исключение TypeError.
- Повторите, в то время как k < len
- Позвольте Pk быть ToString (k).
- Пусть kPresent является результатом вызова внутреннего метода O [[HasProperty]] с аргументом Pk.
- Если kPresent является истинным, то
- Пусть kValue будет результатом вызова внутреннего метода O [[Get]] с аргументом Pk.
- Пусть аккумулятор будет результатом вызова метода [[Call]] внутренний метод callbackfn с неопределенным, как это значение и список аргументов, содержащий аккумулятор, к,-значение, и О.
- Увеличение к на 1.
- Возвратный аккумулятор.
Длина свойство методы уменьшения равно 1.
ПРИМЕЧАНИЕ уменьшает функцию намеренно родовой; он не требует, чтобы это значение было объектом Array. Поэтому он может быть передан другим типам объектов для использования в качестве метода. Может ли функция сокращения успешно применяться к объекту хоста, зависит от реализации.
Который для меня я бы написал (и это не 100% спецификации, но близко) и сохранить в моей личной библиотеке.
function firstToCapital(inputString) {
return inputString.charAt(0).toUpperCase() + inputString.slice(1).toLowerCase();
}
function isClass(inputArg, className) {
return Object.prototype.toString.call(inputArg) === '[object ' + firstToCapital(className) + ']';
}
function checkObjectCoercible(inputArg) {
if (typeof inputArg === 'undefined' || inputArg === null) {
throw new TypeError('Cannot convert argument to object');
}
return inputArg;
};
function ToObject(inputArg) {
checkObjectCoercible(inputArg);
if (isClass(inputArg, 'boolean')) {
inputArg = new Boolean(inputArg);
} else if (isClass(inputArg, 'number')) {
inputArg = new Number(inputArg);
} else if (isClass(inputArg, 'string')) {
inputArg = new String(inputArg);
}
return inputArg;
}
function ToUint32(inputArg) {
return inputArg >>> 0;
}
function throwIfNotAFunction(inputArg) {
if (!isClass(inputArg, 'function')) {
throw TypeError('Argument is not a function');
}
return inputArg;
}
function forEach(array, fn, thisArg) {
var object = ToObject(array),
length,
index;
throwIfNotAFunction(fn);
length = ToUint32(object.length);
for (index = 0; index < length; index += 1) {
if (index in object) {
fn.call(thisArg, object[index], index, object);
}
}
}
function reduce(array, fn, initialValue) {
var object = ToObject(array),
accumulator,
length,
kPresent,
index;
throwIfNotAFunction(fn);
length = ToUint32(object.length);
if (!length && arguments.length === 2) {
throw new TypeError('reduce of empty array with no initial value');
}
index = 0;
if (arguments.length > 2) {
accumulator = initialValue;
} else {
kPresent = false;
while (!kPresent && index < length) {
kPresent = index in object;
if (kPresent) {
accumulator = object[index];
index += 1;
}
}
if (!kPresent) {
throw new TypeError('reduce of empty array with no initial value');
}
}
while (index < length) {
if (index in object) {
accumulator = fn.call(undefined, accumulator, object[index], index, object);
}
index += 1;
}
return accumulator;
}
function keys(object) {
if (!isClass(object, 'object') && !isClass(object, 'function')) {
throw new TypeError('Argument must be an object or function');
}
var props = [],
prop;
for (prop in object) {
if (object.hasOwnProperty(prop)) {
props.push(prop);
}
}
return props;
}
var inputWords = ['Apple', 'Banana', 'Apple', 'Pear', 'Pear', 'Pear'];
var counts = reduce(inputWords, function (previous, element) {
previous[element] = ++previous[element] || 1;
return previous;
}, {});
forEach(keys(counts), function (key) {
console.log(key, this[key]);
}, counts);
На jsFiddle
Конечно, это может быть немного OTT за то, что вы делаете. :)
Это может быть лучший вопрос для http://codereview.stackexchange.com. –
«То есть это работает» --- он не может и не [] [http://jsfiddle.net/7Zwc2/) – zerkms
cc @AndersonGreen. –