JavaScript Sparse Arrays and Empty Slots Guide

A sparse array in JavaScript is an array containing “empty slots” or holes where indices have no assigned value, resulting in an array length greater than the actual number of elements. This article explains what sparse arrays are, how empty slots differ from elements initialized to undefined, and how JavaScript’s built-in iteration methods, loops, and operators treat these holes during execution.

What is a Sparse Array?

A dense array has contiguous elements where every index from 0 to length - 1 contains a defined value. In contrast, a sparse array contains uninitialized slots (often called holes).

You can create a sparse array in several ways:

// Array constructor with a length
const arr1 = new Array(3); // [ <3 empty items> ]

// Array literals with omitted values
const arr2 = [1, , 3]; // [ 1, <1 empty item>, 3 ]

// Deleting an index
const arr3 = [1, 2, 3];
delete arr3[1]; // [ 1, <1 empty item>, 3 ]

// Setting an index beyond current length
const arr4 = [1];
arr4[3] = 4; // [ 1, <2 empty items>, 4 ]

Empty Slots vs. undefined

An empty slot is not the same as an element holding the value undefined. An empty slot means the property key does not exist on the array object at that index:

const sparse = [, ];
const dense = [undefined];

0 in sparse; // false (property does not exist)
0 in dense;  // true  (property exists with value undefined)

sparse.hasOwnProperty(0); // false
dense.hasOwnProperty(0);  // true

How Built-in Methods Treat Empty Slots

JavaScript handles empty slots inconsistently across different ECMAScript specifications. Older methods (mostly ES5) tend to skip empty slots entirely, while modern methods (ES6+) generally treat them as undefined.

1. Methods That Skip Empty Slots

Most classic array transformation and testing methods check whether the index actually exists on the object before executing the callback. They skip empty slots entirely:

const sparse = [1, , 3];

sparse.forEach((val, idx) => console.log(idx, val));
// Output: 
// 0 1
// 2 3

const mapped = sparse.map(x => x * 2);
console.log(mapped); // [ 2, <1 empty item>, 6 ]

const filtered = sparse.filter(() => true);
console.log(filtered); // [ 1, 3 ]

2. Methods That Treat Empty Slots as undefined

Modern ES6+ methods and iterators treat empty slots as if they hold the value undefined:

const sparse = [1, , 3];

console.log(sparse.find(x => x === undefined)); // undefined
console.log(sparse.findIndex(x => x === undefined)); // 1
console.log(sparse.includes(undefined)); // true

console.log([...sparse]); // [ 1, undefined, 3 ]

for (const val of sparse) {
  console.log(val); // Logs 1, undefined, 3
}

3. Special Cases and Conversions

const sparse = [1, , 3];

sparse.indexOf(undefined); // -1
sparse.join("-");          // "1--3"

Summary

When working with JavaScript arrays, empty slots are missing indices rather than initialized undefined values. Legacy iteration methods (forEach, map, filter, reduce) skip holes, whereas modern methods, iterator protocols, and the spread operator normalize holes into undefined.