首页 > 解决方案 > 自定义 reduce 函数实现 - 为什么要传递这个特定的参数列表?

问题描述

我发现这个超级有用的要点。在 reduce 函数实现中,传递了一长串参数:undefined, accumulator, this[i], this, i. 我不明白为什么。据我了解,该行应该是accumulator = callback.call(accumulator, this[i]). 但是,如果我删除任何这些参数,reduce 函数将无法正常工作。请帮助我填补我的理解空白!

 Array.prototype.myReduce = function(callback, initialVal) {
    var accumulator = (initialVal === undefined) ? undefined : initialVal;
    for (var i = 0; i < this.length; i++) {
        if (accumulator !== undefined)
            accumulator = callback.call(undefined, accumulator, this[i], i, this);
        else
            accumulator = this[i];
    }
    return accumulator;
};

//tests
var numbers3 = [20, 20, 2, 3];
var total = numbers3.myReduce(function(a, b) {
    return a + b;
}, 10);
console.log(total); // 55

var flattened = [
    [0, 1],
    [2, 3],
    [4, 5]
].reduce(function(a, b) {
    return a.concat(b);
});
console.log(flattened); //[ 0, 1, 2, 3, 4, 5 ]

标签: javascriptarraysecmascript-6reduce

解决方案


第一个参数callthis要在回调中使用的值。但是这个实现在回调内部没有使用任何this东西,所以第一个参数必须是undefined. 如果你这样做

accumulator = callback.call(accumulator, this[i]).

然后你callback用一个参数调用 the this[i]this回调内部的位置将是accumulator,这绝对不是你想要的)。

也就是说,要点中的实现是不正确的——当最后一个回调返回时,它没有调用回调undefined。它也没有正确检查初始值(可能已作为参数传递,但 is undefined):

// Unaltered function below:
Array.prototype.myReduce = function(callback, initialVal) {
    var accumulator = (initialVal === undefined) ? undefined : initialVal;
    for (var i = 0; i < this.length; i++) {
        if (accumulator !== undefined)
            accumulator = callback.call(undefined, accumulator, this[i], i, this);
        else
            accumulator = this[i];
    }
    return accumulator;
};

// My test:
[1, 2, 3].reduce((a, b, i) => {
  console.log('native reduce iteration!');
  return i === 0 ? b : undefined;
}, 0);

[1, 2, 3].myReduce((a, b, i) => {
  console.log('CUSTOM reduce iteration!');
  return i === 0 ? b : undefined;
}, 0);

要修复它,请无条件调用回调(如果在没有初始值的空数组上调用,请记住抛出错误):

// Unaltered function below:
Array.prototype.myReduce = function(...args) {
    const [callback, initialVal] = args;
    let i;
    let accumulator;
    if (args.length >= 2) {
        accumulator = initialVal;
        i = 0;
    } else if (this.length === 0) {
        throw new TypeError('Reduce called on an empty array with no initial value');
    } else {
        accumulator = this[0];
        i = 1;
    }
    for (; i < this.length; i++) {
        accumulator = callback.call(undefined, accumulator, this[i], i, this);
    }
    return accumulator;
};

// My test:
[1, 2, 3].reduce((a, b, i) => {
  console.log('native reduce iteration!');
  return i === 0 ? b : undefined;
}, 0);

[1, 2, 3].myReduce((a, b, i) => {
  console.log('CUSTOM reduce iteration!');
  return i === 0 ? b : undefined;
}, 0);

console.log(
  [2, 3, 4].myReduce((a, b) => a + b)
);


推荐阅读