DEV Community

Cover image for Array().fill is secretly broken

Posted on

Array().fill is secretly broken

There's something weird about Array().fill(). Well, some of you would say Array itself is weird and confusing depending on how its implemented. You say Array('5') to get an array with '5' as its only element and then when you say Array(5), you get an array with 5 empty slots waiting to be fed. If that's not confusing for you, let's not assume it to be the same for someone new to JavaScript. Anyway, cutting it short. Tell me when would you use the fill() method? You just created an empty array and you want to auto populate it with some data you already have. You want to overwrite your array from some index "x" to index "y" with a given value. I am sure a significant number of us would try and grab the fill() method to get this done. Nothing wrong, nothing bad! That's why we have "Array.prototype.fill()".

Before I tell you where can it go wrong, let us ensure that everyone reading this is on the same page. Try going through the naive snippet that follows and see if you can guess the ouput right. Its so naive that you dont even need to open an editor and test stuff out. If you already feel confident and know how it works feel free to skip to the last paragraph.

const myArray = Array(5); // empty array with 5 slots myArray.fill(); // myArray = [undefined, undefined, ...5 times] myArray.fill(0); // [0,0,0,0,0] myArray.fill('zero', 1, 4); // [0,"zero","zero","zero",0]

Everything looks fine, right? Yes! it does. We tried creating an empty array. We filled it with some number and then we also tried overwriting the array from index 1 to index 3 (4 is not included). What we did not try yet is filling our array with non-primitives. Lets try doing that now.

const myArray = Array(5); // empty array with 5 slots myArray.fill({x:1, y:2}); // [{x:1,y:2}, ...5 times] myArray.fill([1,2]); // [[1,2], [1,2], ...5 times]

Something is burning gif

So far there's no problem but hey! I am here to introduce you to a problem. So if you didn't already smell what's burning, let me show you. Lets say we created an empty array and filled it with 5 empty arrays. Imagine an array 'myArray' with 5 empty arrays inside it. Now lets loop through 'myArray' and try populating all the internal arrays one by one. Take a look at the snippet below and without having it run somewhere tell yourself what do you expect the code to do.

const myArray = Array(5); // empty array with 5 slots myArray.fill([]); // myArray = [[], [], ...5 times] myArray.forEach(arr => { arr.push('x'); }); console.log(myArray); // check the output

You say, we'll get something like an array 'myArray' with 5 arrays inside it, each having one value 'x' inside it? Something like so [ [x], [x], [x], [x], [x] ] ? There lies the problem. Try running the snippet now and see what happens. Weird, right? You get something like so [ [x,x,x,x,x], [x,x,x,x,x], [x,x,x,x,x], [x,x,x,x,x], [x,x,x,x,x] ]. Can you guess what just happened? When you filled your empty 'myArray' with 5 empty arrays, you may have thought that they will be 5 different arrays but that was not the case. Its one array (one reference) that's distributed all across 'myArray'. This is gross. Why would someone want to fill an entire array with the same object? Anyway, this is clearly reflected in the MDN docs if you read the polyfill code to Array.prototype.fill (step 12). Generally, you wouldn't care doing so but then when you get stuck somewhere its always good to checkout what's hidden in the documentation. I was doing some random experiments with radix sort and I enountered this. Even though there are so many other ways you can get this done, let me just share a simple work-around if you really want to use fill to achieve what we tried to achieve in our example.

const yourArray = new Array(5).fill().map(()=> []); // yourArray = [[], [], ...5 times] yourArray.forEach(arr => { arr.push('y'); }); console.log(yourArray); // check the output

Try on Codepen

Originally Posted Here -

Top comments (3)

jfar41 profile image

Way I understand this "error" is due to the pass-by-reference nature of objects in JS. So in

const myArray = Array(5).fill([])
Enter fullscreen mode Exit fullscreen mode

we are filling every slot in outer array(5 total) with a reference to the array passed in .fill()

running the .forEach() runs once for every slot in outer array(5 total) and since every slot has a reference to the same object, we're manipulating on one array but the effect is being shown across its references

endriash profile image
Jędrzej Siewierski

Many thanks for this snippet! It's funny cause i also meet this problem during radix sort implementation. Good work!

mayankav profile image

Totally relatable mate! :)