When you give sort
a function (called a comparator), sort
doesn't make any assumptions about the values (or convert them to anything) at all. It just calls the function you give it repeatedly to compare pairs of elements (call them a
and b
), and uses the function's return value to determine how to order those two elements in the result: If it's 0
, a
and b
are equal; if it's positive, b
should come after a
(e.g., b
is "greater than" a
); if it's negative, b
should come before a
(e.g., b
is "less than a
). So by subtracting a
from b
, the sortLowToHigh
returns the appropriate values.
The convert-to-string behavior you mention only applies when you don't give sort
a comparator to use.
This is covered (in turgid prose) by §15.4.4.11 of the spec.
The order of the calls to the comparator is completely up to the implementation, and different implementations may use different sorting algorithms, so some JavaScript engines may call the comparator more or fewer times, or in a different order, compared to others. For example, given this:
var v = [30,2,1,9,15];
v.sort(function(a, b) {
snippet.log("a = " + a + ", b = " + b);
});
snippet.log(v.join(", "));
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
The current version of Chrome outputs:
a = 30, b = 2
a = 2, b = 1
a = 1, b = 9
a = 9, b = 15
30, 2, 1, 9, 15
...but the current version of Firefox outputs
a = 30, b = 2
a = 2, b = 1
a = 9, b = 15
a = 1, b = 9
30, 2, 1, 9, 15
They both get the same result, of course, or one of them would be violating the spec, but it's fun to see that the order of calls really does vary.