Javascript Array methods such as forEach
have a thisArg
parameter, which is used as the context for invoking the callback:
array.forEach(callback[, thisArg])
as do every
, some
, filter
and map
. However, reduce
and reduceRight
have no such parameter. Is there some particular reason for this, or some reason it is not necessary?
For instance, consider the following implementation of functional composition using 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];
};
}
I would like to make this "this-aware", so the functions being composed are called in the context in which the function returned by compose
is invoked. Currently they appear to be invoked in the context of the global object. I could do the old var self=this;
at the top of function result
, and use that as the first argument to the cur.apply
call, where I currently have this
, but that would be unnecessary if reduce
took a thisArg
argument.
Am I missing something here, and is there something about reduce
that makes this unnecessary or unuseful?
UPDATE
@kangax Yes, that occurred to me. Far be it from me to criticize the design of the API, but the signature for reduce
seems a bit strange to me. The second optional argument functions differently than normal optional arguments, which typically just have a default value; instead its presence or absence changes the behavior, essentially overloading the function based on signature (argument count). When the second parameter is absent, the first element of the array becomes the starting value and the first call to the callback is against the second value. It seems to me that this behavior could be easily emulated by simply calling
array.slice(1).reduce(fn,array[0])
instead of building in special rules for the case where the second argument is omitted, which in turn, if your presumption is correct, also made it essentially impossible to figure out where to specify the thisArg
argument. Then again, I am sure such issues were already debated while the spec was being hashed out, and there may be good reasons for such an approach.
You can always use this method :
... in order to attach a specific context to the callback function.
On the Es-discuss mailing list you find in answer http://article.gmane.org/gmane.comp.lang.javascript.ecmascript4.general/4770
It becomes messy with two optional arguments, since
reduce
(Right
) already covers two functionalities (see Wikipedia), which are distinguished in pure languages (e.g. namedfoldl
andfoldl1
in Haskell). To cite Brendan Eich:I don't think it's a big issue, since these functional higher-order-functions are mostly used with
lamdba-function-expressions anyway (like in your example). Of course there's a little inconsistency, but we can live with that. Image the alternative:Can't be really used, we cannot really determine "if an initialValue was provided" since that means
arguments.length < 2
- we could passundefined
literally as well. So that meanswhich is ugly since we always needed to pass
null
or something tothisArg
if we only wanted an initial value.You noticed that already in your comment to Kangax ("The second optional argument functions differently than normal optional arguments, […] its presence or absence changes the behavior"), but I can't support your statement
as that would a) not work with a complex (chained) expression instead of the
array
variable and b) is cumbersome.