All the answers were correct and reasonable and so I decided to just try it out on my Chrome console. So the answer to the OP is that this is happening because you cannot do a direct fill.
If I understand part of the question is why can't you just do:
const grid = Array(5).fill([])
which is saying okay we have a structure like this:
//[ [ ],
// [ ],
// [ ],
// [ ],
// [ ] ]
And then you are saying go and fill the third one like so:
grid[2].push("third rank item");
But instead of seeing the third one filled with that string, you see all of them. It's simple, you are creating one single array and throwing it in at every location inside of grid. In memory there is only one inner array so you threw that string into that array, it would affect every index.
But I found some behavior slightly different than what I had expected.
If you write the following:
const grid = Array(3).fill([false, false, false]);
You would get the following:
(3) [Array(3), Array(3), Array(3)]
(3) [false, false, false]
(3) [false, false, false]
(3) [false, false, false]
Then if you run: grid[0].push(true), you will get the following:
(3) [Array(4), Array(4), Array(4)]
(4) [false, false, false, true]
(4) [false, false, false, true]
(4) [false, false, false, true]
So modifying one, modifies all, so that's why you cant just do a direct fill and then push as you did, instead you have to do the map() statement like so:
const grid = Array(3)
.fill(null)
.map(() => Array(3).fill(false));
And that will run the inner function three times and each time we generate a brand new and different array.
Array.from(new Array(5), () => []).