我正在尝试通过递归实现 Array.reduce() 方法,它对我有用
var index = 0;
function reduce(arr, fn, initial) {
if (index >= arr.length) {
return initial;
}
if (!initial) {
initial = arr[index];
index++;
}
initial = fn(initial, arr[index], index, arr);
index++;
return reduce(arr, fn, initial);
}
但验证者不接受我的回答。我做错了什么?
最佳答案
一些问题:
- 您定义了
index
作为一个永远不会重置为 0 的全局变量,所以如果reduce
被多次调用,它将具有错误的值。对此有几种解决方案。我建议使用this
跟踪您所在位置的值(value)。 initial
参数是可选的,但您的代码使用!initial
对此进行测试这还不够好:如果initial
的值是 0 或任何其他虚假值,不应将其视为不存在。使用arguments.length
相反。- 当您增加索引时,因为
initial
未提供参数,您不会检查该索引是否变得太大。绝不应该使用无效索引来调用回调函数。交换两个if
声明。 - 当传递一个没有初始值的空数组时,
reduce
返回undefined
,但它应该抛出一个错误,就像 nativereduce
的情况一样方法。 - 当数组有“间隙”(即它是稀疏数组)时,该函数仍然对那些缺失的索引调用回调函数。相反,不应迭代那些不存在的条目。您可以使用数组迭代器来完成此操作,该迭代器仅产生有效的索引。
以下是处理这些问题的方法:
function reduce(arr, fn, initial) {
// Get iterator for the indexes from `this`.
// The first time it will not be an iterator, so get it from arr.keys().
const iter = Object(this).next ? this : arr.keys();
let item = iter.next();
if (arguments.length < 3) { // Solid check for presence of initial argument
// Throw error when there's nothing to reduce
if (item.done) {
throw new TypeError("reduce of empty array with no initial value");
}
initial = arr[item.value];
item = iter.next();
}
// Check end of array only after having dealt with missing initial value
if (item.done) return initial;
initial = fn(initial, arr[item.value], item.value, arr);
return reduce.call(iter, arr, fn, initial); // Pass on the index via `this`
}
// Some test cases
console.log(reduce([1, 2, 3], (a, b) => a+b, 0), "should be 6");
console.log(reduce([1, 2, 3], (a, b) => a+b, 0), "should be 6");
console.log(reduce([1, 2, 3], (a, b) => a+b), "should be 6");
console.log(reduce([1], (a, b) => a+b), "should be 1");
console.log(reduce([1], (a, b) => a+b, 2), "should be 3");
console.log(reduce([], (a, b) => a+b, 1), "should be 1");
try {
console.log(reduce([], (a, b) => a+b), "error");
} catch(e) {
console.log("Error was raised as expected");
}
this
引用是函数私有(private)的,调用者无法看到,因此使用它来传播当前状态(迭代器)似乎是理想的选择。替代方案是:
- 使用额外参数
- 向数组或回调函数参数添加属性。
最后一个选项的缺点是调用者可以检测到此突变。在调用者卡住这些对象的极端情况下,代码甚至会因异常而失败。
所有解决方案都有一个缺陷:原始调用者可以通过初始调用传递状态。因此,对于每个解决方案,调用者可以传递索引(或迭代器)的特定值,如下所示:
- 传递一个带有值的额外参数 - 比方说 5。
- 设置特殊
index
传递给reduce
的函数的属性值为 5。 - 调用
reduce
与call
或apply
,为其提供特定的this
值(迭代器)。
对此我们无能为力,因为要求是使用递归。由于递归要求,该函数在递归调用中无法执行原始调用者无法执行的操作。
关于javascript - array.reduce 通过递归实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50582420/