为什么Array.prototype.reduce没有thisObject参数?

17

Javascript数组方法,例如forEach,具有thisArg参数,该参数用作调用回调函数的上下文:

array.forEach(callback[, thisArg])

就像everysomefiltermap一样,reducereduceRight并没有这种参数。这是有特别的原因,还是它不必要呢?

例如,考虑使用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返回的函数时在上下文中被调用。目前它们似乎在全局对象的上下文中被调用。我可以在result函数顶部执行旧的var self=this;,并将其用作cur.apply调用的第一个参数,其中我当前有this,但如果reduce接受thisArg参数,则这将是不必要的。

我错过了什么吗?还有关于reduce的一些特性使这个方法不必要或无用吗?

更新

@kangax 是的,我也想到了这一点。虽然远非我批评API设计的能力,但reduce的签名对我来说有点奇怪。第二个可选参数的功能与正常的可选参数不同,通常只有一个默认值;相反,它的存在或不存在会改变行为,实质上基于签名(参数计数)重载函数。当第二个参数缺失时,数组的第一个元素成为起始值,并且对回调的第一次调用是针对第二个值。在我看来,这种行为可以通过简单地调用

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

与其在第二个参数被省略的情况下建立特殊规则,进而如果你的推测是正确的,也基本上不可能弄清楚在哪里指定thisArg参数。不过,我确信这样的问题在规范制定期间已经被讨论过了,并且可能有很好的理由采取这种方法。


可能是因为reduce函数的第二个参数是初始值。 - kangax
你需要使用reduceRight的原因是什么?我认为你可以通过对一个反转后的数组使用forEach来轻松实现。编辑:我的意思是,你不是要减少函数数组,而是要将一组输入运行通过这些函数。 - tJener
1
@tJener 当然你可以使用forEach(就像underscore.js一样),但我认为使用reduce/Right更加优雅。 - user663031
4个回答

6
你可以始终使用这种方法:
array.reduce(callback.bind(context), initialValue);

为了将特定上下文附加到回调函数中,需要进行如下操作。

4
有两个可选参数时会变得混乱,因为reduceRight)已经涵盖了两个功能(请参见维基百科),这在纯语言中是有区别的(例如在Haskell中命名为foldlfoldl1)。引用Brendan Eich的话:

这意味着reduce需要一个回调参数和两个可选参数:thisObject和init。哪一个应该先来?更常见的是init,但是你将回调参数与“thisObject”参数分开,这也许可以接受。多个可选参数这种方式有点混乱......

或者,我们可以消除额外的“thisObject”参数,因为人们可以始终使用绑定。

我认为这不是一个大问题,因为这些函数式高阶函数通常与lamdba-函数表达式一起使用(就像你的例子一样)。当然存在一些不一致性,但我们可以接受。想象一下替代方案:

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

这个值不能真正使用,因为我们无法确定是否提供了初始值 "如果提供了初始值",因为这意味着 arguments.length < 2 - 我们也可以直接传递undefined。所以这意味着

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

这很丑陋,因为如果我们只想要一个初始值,就必须传递 null 或其他内容给 thisArg

你在评论Kangax时已经注意到了这一点("第二个可选参数的功能与普通可选参数不同,[…] 它的存在或不存在会改变行为"),但我不能支持你的说法。

这个行为可以通过简单调用 array.slice(1).reduce(fn,array[0]) 来轻松模拟。

然而,这不仅对于复杂(链式)表达式替代 array 变量无法运作,而且也很繁琐。


4
在Es-discuss邮件列表中,您可以在此处找到答案。 回调函数的其他方法之所以需要'thisArg'并不是因为它是必需或有用的,而是为了兼容性,因为现有实现已经提供了这些函数。

搜索结果不错,但我发现该线程中的其他邮件更相关。 - Bergi
在这篇帖子发布时(2009年3月),ECMAScript 5 已经非常稳定,并且只进行了一些编辑性的更改。我们今天所知的 JavaScript 在2008年末就已经确定,并且任何设计理念也都被决定了。这是我对原帖问题的理解。 - Wolfgang Kuehn

1

不需要thisObject参数,因为你可以在累加器中添加一种thisObject,并使用初始值传递thisObject。

示例:

thisObj = { some data }  // kind of this arg to be used in the reducer
const { view, used, totals } = sheetConfig.reduce(reducer, {
  view: [],    // initial  
  used: [],    // another initial
  totals: [],  // and one more initial
  thisObj      // and ...
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接