Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 347
AndyRightNow Avatar asked Aug 14 '16 08:08

AndyRightNow


1 Answers

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 it 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 either Array.from or fill with map:

// Array.from
var matrix = Array.from({length: 10}, function() {
    return new Array(10).fill(0);
});

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

or in ES2015:

// Array.from
let matrix = Array.from({length: 10}, () => new Array(10).fill(0));

// fill and map
let matrix = new Array(10).fill().map(() => new Array(10).fill(0));
like image 93
T.J. Crowder Avatar answered Nov 15 '22 09:11

T.J. Crowder