问题
I'm trying to concat an array of arrays using reduce and I figured that I could use the Array.prototype.concat function like this:
arr = [[1],[2],[3]]
arr.reduce((a, b) => Array.prototype.concat(a, b), [])
Which works fine and gives me the array [1, 2, 3]
. Then I thought I could be even smarter and do it like this:
arr = [[1],[2],[3]]
arr.reduce(Array.prototype.concat, [])
This however gives me an error:
TypeError: Array.prototype.concat called on null or undefined
at Array.reduce (native)
at Object.<anonymous> (/home/axel/Developer/temp/reduce2.js:2:5)
at Module._compile (module.js:556:32)
at Object.Module._extensions..js (module.js:565:10)
at Module.load (module.js:473:32)
at tryModuleLoad (module.js:432:12)
at Function.Module._load (module.js:424:3)
at Module.runMain (module.js:590:10)
at run (bootstrap_node.js:394:7)
at startup (bootstrap_node.js:149:9)
It seems to think that Array.prototype.concat
is undefined
. Why is this?
回答1:
concat
operates as a method with respect to some object (i.e., the this
value of the method's execution). When you pass a function into a function, you do not pass along any this
value. Thus, you're effectively doing something similar to:
var rawConcat = Array.prototype.concat;
rawConcat(a,b);
You can use bind
to create a copy of a function with a particular this
burned into it:
arr.reduce(Array.prototype.concat.bind(Array.prototype), [])
However, now that that's cleared up, there are several other issues that stop you from doing this.
For one, reduce
actually gets four arguments, including the current index and the whole array. You ignore these by having your (a,b)=>
lambda only pass two of those four arguments into concat
. That's fine, but when you supply a function directly as an argument to reduce
, it will use all four arguments, so you'll get the result of the call Array.prototype.concat(a, b, currentIndex, arr)
.
Furthermore, what you're doing isn't a sensible use of Array.prototype
. The concat
function concatenates its arguments and appends them to a copy of the this
value. Since Array.prototype
is itself just an empty array (albeit with many own-properties that other arrays use as inherited properties), this is effectively the same as [].concat(a,b)
or (perhaps even more readably) a.concat(b)
.
回答2:
Array.prototype.concat
expects the context (this
) to be an Array and in your first example the context is actually Array.prototype
which incidentally looks exactly like an Array, hence it works.
Your second example however, passes the concat function by reference so the context is null or undefined.
The more correct way to do this would be to use Function.prototype.call
to bind the context to one of the arrays or call the method directly on the array, e.g.
arr = [[1],[2],[3]];
arr.reduce((a, b) => Array.prototype.concat.call(a, b), []);
// Or
arr.reduce((a, b) => a.concat(b), []);
来源:https://stackoverflow.com/questions/42541207/using-prototype-functions-in-higher-order-functions-in-javascript