Skip to main content

Array Initialization

Initializing arrays in JavaScript is a crucial task, with many techniques to choose from and performance considerations to keep in mind. While there might not be a one-size-fits-all solution, there are a few options you might want to consider.

Array() Constructor

The first thing you’d reach for would probably be the Array() constructor. Counterintuitively, this is probably the most problematic option to use on its own. While it works for any number of arguments to create an array with the given values, it falls short pretty much everywhere else.

The Problem with Empty Slots

Most of its problems stem from holes or “empty” values with which the resulting array is populated and how these are handled elsewhere.
const arr = Array(3); // [ , , ] - 3 empty slots
arr.map(() => 1); // [ , , ] - map() skips empty slots
arr.map((_, i) => i); // [ , , ] - map() skips empty slots
arr[0]; // undefined - actually, it is an empty slot
The Array() constructor creates arrays with empty slots that are skipped by most array methods like map(), filter(), and forEach().

Array.from()

Array.from() is a static method that creates a new, shallow-copied Array instance from an array-like or iterable object. It is very useful for converting array-like objects (e.g. arguments, NodeList) or iterables (e.g. Set, Map, Generator) into actual arrays.

Creating Arrays with Length

Apart from that, it can easily be “tricked” into creating an array of a given length by passing an object with a length property. This is somewhat slow, but it works well and circumvents some of the problems of the Array() constructor.
const arr = Array.from({ length: 3 }); // [undefined, undefined, undefined]
arr.map(() => 1); // [1, 1, 1]
arr.map((_, i) => i); // [0, 1, 2]

With Mapping Function

Additionally, it allows you to pass a mapping function as a second argument, which is very useful for initializing arrays with values.
const staticArr = Array.from({ length: 3 }, () => 1); // [1, 1, 1]
const indexArr = Array.from({ length: 3 }, (_, i) => i); // [0, 1, 2]
Array.from() is the most flexible option for creating arrays, especially when you need a mapping function.

Array.prototype.fill()

While Array.from() is quite flexible, using a mapping function to fill it with the same value isn’t particularly efficient. Array.prototype.fill() comes to fill this gap by allowing you to fill an existing array with the same value.
const nullArr = new Array(3).fill(null); // [null, null, null]
const staticArr = Array.from({ length: 3 }).fill(1); // [1, 1, 1]
const indexArr = Array(3).fill(null).map((_, i) => i); // [0, 1, 2]
This can also come in handy in conjunction with the Array() constructor, as it allows you to fill the array with a value, instead of empty slots.

Array.prototype.map()

Array.from() allows for a mapping function via a second argument, but a lot of people think it’s hard to read. Additionally, there are a few edge cases, where having access to the array itself during mapping can be useful.

Advanced Mapping

Array.prototype.map() gives you this little bit of extra flexibility and readability if that’s what you’re concerned about.
const arr = Array(3).map(() => 1); // [ , , ] - map() skips empty slots
const staticArr = Array.from({ length: 3 }).map(() => 1); // [1, 1, 1]
const indexArr = Array.from({ length: 3 }).map((_, i) => i); // [0, 1, 2]
const fractionArr =
  Array.from({ length: 3 }).map((_, i, a) => i / a.length); // [0, 0.5, 1]
Remember that Array.prototype.map() doesn’t work well with empty values created by the Array() constructor.

Performance Recommendations

Performance might be a concern if this sort of operation is very common in your application, but overall none of these options are particularly slow. The Array() constructor seems to be the fastest. That being said, if combined with Array.prototype.fill(), it can be the best option for initializing an array with a single value. Oddly enough, this performance advantage still holds even if you chain an Array.prototype.map() call afterwards to create dynamic values.
const initializeArrayWithValues = (n, val = 0) => Array(n).fill(val);
const initializeMappedArray = (n, mapFn = (_, i) => i) =>
  Array(n).fill(null).map(mapFn);

initializeArrayWithValues(4, 2); // [2, 2, 2, 2]
initializeMappedArray(4, (_, i) => i * 2); // [0, 2, 4, 6]

Summary

Use Array(n).fill(value) for the best performance when filling with a single value.
Use Array(n).fill(null).map(fn) for creating arrays with dynamic values based on index.
Use Array.from({ length: n }, fn) for the most readable code with a mapping function.
Use Array.from(iterable) to convert array-like objects or iterables to arrays.

Build docs developers (and LLMs) love