JavaScript递归实现Array.reduce方法异常,求技术解决方案
Hey there! Let's break down what's going wrong with your recursive reduce2 implementation and fix it step by step.
The Issues in Your Current Code
Your implementation has a few critical bugs that are causing those unexpected outputs:
Wrong Recursive Parameter Order
Yourreduce2function is defined to take(fn, initial)as arguments, but when you recurse, you callreduce(tail, fn, result). This swaps the parameters entirely—you're passing the arraytailas the first argument (which should be the callbackfn), and the actualfnas the second argument (which should be the initial accumulator). This messes up all subsequent recursive calls, leading to weird returns like the original function itself.No Termination Condition
There's no check for when the array is empty, so the recursion will keep running indefinitely (or until it hits a runtime error) instead of returning the final accumulated value.Incorrect Handling of Missing Initial Value
Nativereduceuses the first element of the array as the initial accumulator if noinitialis provided, then starts iterating from the second element. Your code always usesinitial(even if it'sundefined) with the first element, which breaks the native behavior.Wrong Callback Arguments
The nativereducecallback receives(accumulator, currentValue, currentIndex, originalArray), but you're passing(initial, head, 0, tail)—the index is stuck at 0, and you're passing the remaining tail instead of the original array.
Fixed Recursive reduce2 Implementation
Here's a corrected version that matches native reduce behavior:
Array.prototype.reduce2 = function(fn, initial) { // Handle empty array with no initial value (matches native error) if (this.length === 0) { if (arguments.length < 2) { throw new TypeError('Reduce of empty array with no initial value'); } return initial; } // Destructure first element and remaining array const [first, ...rest] = this; let nextAccumulator; if (arguments.length >= 2) { // We have an initial value: calculate next accumulator with first element nextAccumulator = fn(initial, first, 0, this); } else { // No initial value: use first element as accumulator, recurse on rest return rest.reduce2(fn, first); } // Recurse on the remaining array with the new accumulator return rest.reduce2(fn, nextAccumulator); };
Testing It With Your Examples
Let's verify this fixes your test cases:
a.reduce(add)anda.reduce2(add)both return10a.reduce(add, 10)anda.reduce2(add, 10)both return20a.reduce(add, undefined)anda.reduce2(add, undefined)both returnNaNa.reduce(mul)anda.reduce2(mul)both return24a.reduce(foo, '')anda.reduce2(foo, '')both return1234
Key Fixes Explained
- Recursive Parameter Order: We now call
rest.reduce2(fn, nextAccumulator), passing the callback first, then the updated accumulator—matching the function's parameter definition. - Termination Condition: When the array is empty, we return the accumulated value (or throw an error if there's no initial value, just like native
reduce). - Initial Value Handling: If no initial value is provided, we use the first array element as the accumulator and start recursing from the rest of the array.
- Correct Callback Args: We pass the correct current index (starting at 0 for the first element) and the original array (
this) to the callback.
内容的提问来源于stack exchange,提问作者Anton Zatolokin

