Modern JavaScript

Best of Modern JavaScript — Array Holes and Operations

Spread the love

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at some array operations with holes.

Array Operations and Holes

Various operations treat array holes differently.

Iteration is done with the Array.prototype[Symbol.iterator] call.

It treats holes as if the entry is undefined .

We can see this if we call Symbol.iterator directly.

For instance, we can write:

const arr = [, 'foo'];
const iter = arr[Symbol.iterator]();


We return the iterator from to let us get the elements of the array sequentially with an iterator.

If we call next the first time, we get:

{ value: undefined, done: false }

And if we call next again, we get:

{ value: "foo", done: false }

As we can see, the value is undefined is returned from the first next call.

The spread operator also treats holes the same way.

For instance, we can write:

const arr = [, 'foo'];

After we spread the arr array, we can see the logged value is:

[undefined, "foo"]

The for-of loop also treats holes the same way.

For instance, if we write:

const arr = [, 'foo'];

for (const x of arr) {

then we get undefined and 'foo' .

Array.from() uses iteration to convert an iterable object to an array.

This works exactly like the spread operator.

For instance, if we have:

const arr = [, 'foo'];


Then we get:

[undefined, "foo"]

If we pass in an array-like object that isn’t iterable into the Array.from method, the missing entry is still treated as undefined .

If we have:

const arr = Array.from({
  1: 'foo',
  length: 2

then we get the same result.

With a second argument, Array.from works like .

For example, if we write:

const arr = Array.from([, 'foo'], x => x)


Then arr is [undefined, “foo”] .

We can also use it to get the index. To do that, we write:

const arr = Array.from([, 'foo'], (x, i) => i)

then arr is [0, 1] . skip holes, but preserve them.

For instance, if we have:

const arr = [, 'foo'].map(x => x)


Then we get:

[empty, "foo"]

The hole stays as an empty slot after mapping.

It acts the same way if we map an array with holes to an array of indexes.

If we write:

const arr = [, 'foo'].map((x, i) => i)

we get [empty, 1] .

The behavior of array holes is treated differently by different array instance methods.

forEacg , filter , and some ignore holes.

map skips but preserves holes.

join and toString treat holes like they’re undefined ,

null and undefined are treated as empty strings.

copyWithin creates a hole when copying holes.

entries , keys , values, find, and findIndex treat each hole as if they’re undefined .

fill doesn’t care whether there are holes or not.

concat , map , push , reverse , slice , sort , splice and unshift all preserve holes.

pop and shift treat them as elements.


Different operators and methods treat array holes differently.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *