Reputation: 1616
I have a string, " test "
. It's a really ugly string, so let's trim it.
" test ".trim()
returns "test"
. Nice!
Now let's try to do it with that string as an argument.
String.prototype.trim.call(" test ")
also returns "test"
. Nice again!
Oh, that means I can use String.prototype.trim.call
to map an array of ugly strings by their trimmed counterparts, right?
[" test "].map(String.prototype.trim.call)
does not return ["test"]
.
It throws TypeError: undefined is not a function
.
Why is this not allowed?
Upvotes: 0
Views: 46
Reputation: 224904
All function call
methods are the same function value:
> String.prototype.trim.call === Function.prototype.call
true
> String.prototype.trim.call === alert.call
true
The function to be called is passed to Function.prototype.call
as its this
value. The this
value for a function call varies depending on how the call is made:
f(); // calls f with this === undefined
x.f(); // calls x.f with this === x
x['f'](); // same as previous
f.call(y); // calls f with this === y
f.apply(y, []); // same as previous
When referencing a function in all cases that aren’t calls, there is no this
value involved.
const f = x.f; // no association with x is maintained
x(); // calls f with this === undefined
So, when you pass String.prototype.trim.call
to Array#map
, you’re passing the function Function.prototype.call
, with no relationship to String.prototype.trim
. Array#map
then calls it with the thisArg
given as the second argument (undefined
, since you only passed one argument). Function.prototype.call
calls the this
value it was given, as it does, and fails because it can’t call undefined
.
The code is fixable by passing the correct value of this
as thisArg
:
[" test "].map(String.prototype.trim.call, String.prototype.trim)
Pretty verbose, huh? You can abuse the fact that all methods from prototypes are equal to make it shorter (Set
being a built-in function with a short name):
[" test "].map(Set.call, ''.trim)
but even that’s no shorter than the usual, readable way:
[" test "].map(x => x.trim())
which has the bonus of not forwarding unexpected arguments.
Upvotes: 1