12

var arr = new Array(4).fill({});

arr[2].status = true;

console.log(arr[0].status); 

why is array fill filling same object in all indexes?

Shishir Arora
  • 5,521
  • 4
  • 30
  • 35
  • 6
    Because you're inserting the same object to every cell within the array. – Sirko Apr 08 '17 at 13:15
  • 1
    Go with `Array.from({length: 4}, () => {})` if you want a new object for each item. – loganfsmyth Apr 08 '17 at 20:54
  • _"why is array fill filling same object in all indexes"_ That's like asking "why does console.log('hello') log hello"_. I don't understand your confusion. – a better oliver Apr 10 '17 at 12:38
  • see [Changing one cell in multidimensional array updates entire column](https://stackoverflow.com/q/34118571/1048572) – Bergi Aug 31 '17 at 03:07

2 Answers2

14

.fill will insert the same exact object (same instance) on each segment of the array.
That's why doing .fill(Math.random) will return an array filled with always the same number.

You can do this to obtain what you want:

new Array(4).fill().map(() => ({}));

To explain what's happening under the hood in your question's code, let's convert this to ES5 code:

var arr = new Array(4);  // new Array(4)
var obj = new Object();  // *1: with this object instance
for(i = 0; i < 4; i++) { // .fill( *1 )
  arr[i] = obj;
}

As you can see, we are assigning the same object instance (obj) to each cell of the array instance.

This is the same principle of why:

const obj = {};
const a = obj;
const b = obj;

a.foo = true;

Makes a.foo === b.foo resolve to true.

Fez Vrasta
  • 14,110
  • 21
  • 98
  • 160
11

fill repeats the value you pass it. The value in this case is an object reference. Every copy of that reference refers to the same object, so what you're getting is:

       +−−−−−−−−−+
arr−−−>| (array) |
       +−−−−−−−−−+         +−−−−−−−−−−−−−−+
       | 0       |−−+−+−+−>|   (object)   |
       | 1       |−/ / /   +−−−−−−−−−−−−−−+
       | 2       |−−/ /    | status: true |
       | 3       |−−−/     +−−−−−−−−−−−−−−+
       +−−−−−−−−−+       

If you want separate objects, you'll need to create multiple objects. The simplest way is, of course:

var arr = [{}, {}, {}, {}];

Example:

var arr = [{}, {}, {}, {}];
arr[2].status = true;
console.log("arr[0].status", arr[0].status);
console.log("arr[2].status", arr[2].status);

If you want to do it with a variable length:

Since you're using Array.fill, I'm assuming you're using ES2015 (aka "ES6") features (but see below for an ES5-compatible solution without polyfills). You can do that via Array.from with a callback:

const arr = Array.from({length:4}, () => ({})); 
arr[2].status = true;
console.log("arr[0].status", arr[0].status);
console.log("arr[2].status", arr[2].status);

That gives you:

                    
       +−−−−−−−−−+       
arr−−−>| (array) |       
       +−−−−−−−−−+         +−−−−−−−−−−−−−−+
       | 0       |−−−−−−−−>|   (object)   |
       | 1       |−−−−−+   +−−−−−−−−−−−−−−+
       | 2       |−−−+ |   
       | 3       |−+ | |   +−−−−−−−−−−−−−−+
       +−−−−−−−−−+ | | +−−>|   (object)   |
                   | |     +−−−−−−−−−−−−−−+
                   | |     
                   | |     +−−−−−−−−−−−−−−+
                   | +−−−−>|   (object)   |
                   |       +−−−−−−−−−−−−−−+
                   |       | status: true |
                   |       +−−−−−−−−−−−−−−+
                   |                      
                   |       +−−−−−−−−−−−−−−+
                   +−−−−−−>|   (object)   |
                            +−−−−−−−−−−−−−−+

(You can do that with an Array.from polyfill on ES5 if you like, just use function() { return {}; } instead of () => ({}).)


In ES5, if you need a variable length, the simplest thing is probably just a loop:

var arr = [];
for (var i = 0; i < 4; ++i) {
    arr[i] = {};
}
arr[2].status = true;
console.log("arr[0].status", arr[0].status);
console.log("arr[2].status", arr[2].status);

You could put that in a helper function, of course.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875