I've been working through Eloquent JavaScript's exercises and found out something I think is odd. I wrote a trivial array-flattening piece of code:
var arrays = [[1, 2, 3], [4, 5], [6]];
var out = arrays.reduce(function(acc, next){ return acc.concat(next); });
console.log(out);
// → [1, 2, 3, 4, 5, 6]
So far so good. But that didn't seem pretty to me, so I rewrote it as:
var arrays = [[1, 2, 3], [4, 5], [6]];
var my_concat = function(acc, next){ return acc.concat(next); }
var out = arrays.reduce(my_concat);
console.log(out);
// → [1, 2, 3, 4, 5, 6]
It was better, but do we really need to introduce a function, be it anonymous or named, to do such a basic thing? Array.prototype.concat.call's call signature is exactly what we need! Feeling smart, I rewrote the code again:
var arrays = [[1, 2, 3], [4, 5], [6]];
var out = arrays.reduce([].concat.call);
// → TypeError: arrays.reduce is not a function (line 2)
Well, that haven't turned out as I expected. The error message seemed cryptic to me.
I decided to investigate. This works:
var arrays = [[1, 2, 3], [4, 5], [6]];
var my_concat = function(acc, next){ return [].concat.call(acc,next); }
var out = arrays.reduce(my_concat);
console.log(out);
// → [1, 2, 3, 4, 5, 6]
And this also works:
var arrays = [[1, 2, 3], [4, 5], [6]];
arrays.my_concat = function(acc, next) { return [].concat.call(acc, next); }
var out = arrays.reduce(arrays.my_concat);
console.log(out);
// → [1, 2, 3, 4, 5, 6]
More tinkering in the console:
[].concat.call
// → call() { [native code] }
typeof [].concat.call
// → "function"
[].concat.call([1, 2, 3], [4, 5])
// → [1, 2, 3, 4, 5]
var cc = [].concat.call
cc
// → call() { [native code] }
typeof cc
// → "function"
cc([1, 2, 3], [4, 5])
// → Uncaught TypeError: cc is not a function(…)
And even this works:
Array.prototype.my_concat = function(acc, next) { return [].concat.call(acc, next); }
// → function (acc, next) { return [].concat.call(acc, next); }
[[1, 2, 3], [4, 5], [6]].reduce([].my_concat)
// → [1, 2, 3, 4, 5, 6]
[[1, 2, 3], [4, 5], [6]].reduce([].concat.call)
// → Uncaught TypeError: [[1,2,3],[4,5],[6]].reduce is not a function(…)
Is there something special about built-in functions like .call?
[].concat.callwould append all the arguments to the output, and [].reduce doesn't just pass the 2 arguments you use, it passes the index and the whole input array as well, so the output would be cluttered.[[1, 2, 3], [4, 5], [6]].reduce( (a,b)=>a.concat(b) )... in theory, i would like to use natives as well, but if you have to bind(), re-apply(), or mangle prototypes, then i think a simple anon callback is more semantic, even if it's not as hard-core...