I'm reading javascript the good parts, and the author gives an example that goes like so:
['d','c','b','a'].sort(function(a,b) {
return a.localeCompare(b);
});
Which behaves as expected. Now I have tried to do something like this - which is the next logical step:
['d','c','b','a'].sort(String.prototype.localeCompare.call);
And that fails with an error:
TypeError: object is not a function
Now I left wondering why... Any ideas?
I'm reading javascript the good parts, and the author gives an example that goes like so:
['d','c','b','a'].sort(function(a,b) {
return a.localeCompare(b);
});
Which behaves as expected. Now I have tried to do something like this - which is the next logical step:
['d','c','b','a'].sort(String.prototype.localeCompare.call);
And that fails with an error:
TypeError: object is not a function
Now I left wondering why... Any ideas?
Share Improve this question asked Dec 11, 2011 at 1:20 Leon FedotovLeon Fedotov 7,8715 gold badges31 silver badges33 bronze badges4 Answers
Reset to default 6call
needs to be bound to localeCompare
:
['d','c','b','a'].sort(Function.prototype.call.bind(String.prototype.localeCompare));
The reason you are having a problem is that you are passing sort
Function.prototype.call
. As you may know, when no this
is otherwise provided, it will be the global object (window
in browser environments). Thus, when sort
tries to call the function passed to it, it will be calling call
with this
set to the global object, which in most (all?) cases is not a function. Therefore, you must bind call
so this
is always localeCompare
.
The reason it doesn't work is that String.prototype.localeCompare.call
returns a reference to Function.prototype.call
not to String.prototype.localeCompare
as you might think.
You're passing a reference to the call
function without retaining any relationship to the localeCompare
method you intend it to be called upon. So sort
will invoke the function referenced byString.prototype.localeCompare.call
, but it will invoke it in the global context, not as the method of some function object.
As @icktoofay pointed out, you need to bind the call
function to localeCompare
. Here's a much oversimplified demonstration of how this is done without the bind
method:
function simpleBind(fn, _this) {
return function() {
return fn.apply(_this, arguments);
}
}
['d','c','b','a'].sort(simpleBind(Function.prototype.call,
String.prototype.localeCompare));
Ok, solved it! this does the trick:
['d','x','z','a'].sort(String.prototype.localeCompare.call.bind(String.prototype.localeCompare))
Cause when you think about it the sort function actually calls apply on call so it needs to be bound to the string object in order to execute in scope. ^_^