To find the tallest kid in a group, you start with a height so low that anyone can beat it, and every taller kid becomes the new tallest so far. That starting height is minus infinity. If no kids show up, the starting height is all you have. Finding the smallest works the other way round and starts at infinity.
The curse
console.log(Math.max()); // prints: -Infinity
console.log(Math.min()); // prints: Infinity
console.log(Math.max() < Math.min()); // prints: true
Call them with nothing inside, and max is smaller than min.
Why it happens
Math.max looks at its arguments one by one and keeps the biggest so far. Before it has seen anything, it needs a starting value that any number will beat. The only value that works for every possible number is -Infinity. When you pass nothing, nothing ever replaces that starting value, so it is what you get back.
Math.min is the mirror image. It starts at Infinity, since every number is smaller than that.
This choice keeps the math consistent. The biggest number in a group should not change when you add an empty group to it, and -Infinity is the only starting value that never wins against a real number.
console.log(Math.max(3, 7, 2)); // prints: 7
console.log(Math.max(-500)); // prints: -500With an array
Math.max wants separate numbers, not a list. Don't forget the three dots.
const heights = [3, 7, 2];
console.log(Math.max(heights)); // prints: NaN
console.log(Math.max(...heights)); // prints: 7
When you pass the array itself, Math.max gets one argument and tries to turn it into a number. An array becomes text first, "3,7,2", and that text is not a number, so the result is NaN. The three dots of the spread operator unpack the array into separate arguments, so the call becomes Math.max(3, 7, 2).
There is a sneaky detail here. An array with one item, like [5], becomes the text "5" and then the number 5, so Math.max([5]) happens to work. Code like that can pass a quick test with one item and break as soon as there are two.
Bonus twist: empty arrays
Spreading an empty array is the same as passing nothing, so you are back to minus infinity.
const noKids = [];
console.log(Math.max(...noKids)); // prints: -Infinity
If your list can be empty, check before you trust the answer, and decide what "no answer" should look like in your program.
const scores = [];
const best = scores.length > 0 ? Math.max(...scores) : null;
console.log(best); // prints: null
One more practical tip. For very long arrays, hundreds of thousands of items, spreading can hit the engine's limit on how many arguments a function can take. For data that big, a plain loop or reduce is the safer choice.
Rule of thumb
Spread your array into Math.max(), and make sure it isn't empty.
Writing Math.max(scores) with an array. It gives NaN for most arrays. Write Math.max(...scores) so each item becomes its own argument.
Check yourself
Spreading an empty array passes no arguments, and Math.max with nothing to compare returns its starting value, -Infinity.