AndyRightNow AndyRightNow - 3 months ago 18
Javascript Question

JavaScript - why Array.prototype.fill actually fills a "pointer" of object when filling anything like 'new Object()'

I was trying to used Array.prototype.fill method to create a n x n 2D array but the code was buggy and I eventually found out all arrays inside are actually "pointers" to the same array.

Sample:

var matrix = new Array(10).fill(new Array(10), 0);


I thought conceptually this could create a 10 x 10 2D array. However if I assign value to matrix[0][0]:

matrix[0][0] = 1;


The result will actually be:

matrix[0][0] === 1;
matrix[1][0] === 1;
matrix[2][0] === 1;
matrix[3][0] === 1;
matrix[4][0] === 1;
matrix[5][0] === 1;
matrix[6][0] === 1;
matrix[7][0] === 1;
matrix[8][0] === 1;
matrix[9][0] === 1;


and every time if I tried to assign value to any of the position in the matrix, all corresponding positions in other sub-arrays will change as well.

I am wondering why it's happening?

Can anyone please answer this head-scratching question?

Answer

I am wondering why it's happening?

Array#fill takes the value you give it as the first argument, and fills the array with copies of that value.

The value you're giving it is a reference to an array, so naturally what gives you back is an array filled with copies of that reference. Not copies of the array, copies of the reference.

E.g., it behaves this way for exactly the same reason this code:

var a = new Array(10);
var b = a;

...leaves us with a and b both referring to the same array (both containing the same value; a reference to the single array we've created).

Let's throw some Unicode-art at it:

After this code runs:

var a = new Array(10);
var b = a;

we have this in memory (minus a few irrelevant details):

a:Ref89895−−−+
             |
             |      +−−−−−−−−−−−−−−−+
             +−−−−−>|     array     |
             |      +−−−−−−−−−−−−−−−+
             |      | length: 10    |
b:Ref89895−−−+      +−−−−−−−−−−−−−−−+

a and b contain a reference, which I've shown here as Ref89895 although we never see the actual value. That's what's copied by b = a, not the array itself.

Similarly, when you do:

var matrix = new Array(10).fill(new Array(10), 0);

you end up with

                   +−−−−−−−−−−−−−−−+
matrix:Ref89895−−−>|     array     |
                   +−−−−−−−−−−−−−−−+
                   | length: 10    |
                   | 0: Ref55462   |--\
                   | 1: Ref55462   |--\\
                   | 2: Ref55462   |--\\\
                   | 3: Ref55462   |--\\\\    +−−−−−−−−−−−−−−−+
                   | 4: Ref55462   |---+++++->|     array     |
                   | 5: Ref55462   |--/////   +−−−−−−−−−−−−−−−+
                   | 6: Ref55462   |--////    | length: 10    |
                   | 7: Ref55462   |--///     +−−−−−−−−−−−−−−−+
                   | 8: Ref55462   |--//
                   | 9: Ref55462   |--/
                   +−−−−−−−−−−−−−−−+

To create a 10-place array where each of the 10 places is itself a 10-place array of 0, I'd probably use fill with map:

var matrix = new Array(10).fill().map(function() {
    return new Array(10).fill(0);
});

or in ES2015:

let matrix = new Array(10).fill().map(() => new Array(10).fill(0));