How Array.prototype.includes Works in JavaScript

Array.prototype.includes() is a built-in JavaScript method that checks if an array contains a specified value, returning a boolean result (true or false). This article explains the underlying mechanism of Array.prototype.includes(), focusing on its use of the SameValueZero comparison algorithm, its handling of search offsets with fromIndex, and how it evaluates different data types such as primitives, NaN, and object references.

The SameValueZero Algorithm

Under the hood, JavaScript’s Array.prototype.includes() determines element existence using the SameValueZero equality algorithm defined in the ECMAScript specification. This differentiates it from both strict equality (===) and the indexOf() method, which uses the IsStrictlyEqual algorithm.

The primary differences in SameValueZero are:

  1. NaN Handling: In standard JavaScript equality (===), NaN === NaN evaluates to false. Consequently, indexOf(NaN) returns -1. However, SameValueZero treats NaN as equal to NaN. Therefore, [NaN].includes(NaN) returns true.
  2. Zero Handling: Both +0 and -0 are considered equal, meaning [-0].includes(+0) evaluates to true.
const numbers = [1, 2, NaN, -0];

// NaN evaluation
console.log(numbers.includes(NaN)); // true
console.log(numbers.indexOf(NaN));  // -1

// Signed zero evaluation
console.log(numbers.includes(+0));  // true

Search Parameters and Iteration

The includes() method accepts two arguments:

array.includes(searchElement, fromIndex)

Offset Behavior (fromIndex)

The search iteration processes elements sequentially based on fromIndex:

const items = ['a', 'b', 'c', 'd'];

console.log(items.includes('b', 1));  // true (starts at index 1)
console.log(items.includes('b', 2));  // false (starts at index 2)
console.log(items.includes('c', -2)); // true (starts at index 2)
console.log(items.includes('a', -10)); // true (clamped to index 0)

Primitive Types vs. Reference Types

Array.prototype.includes() compares elements by value for primitives and by reference for objects.

const obj = { id: 1 };
const list = [obj, { id: 2 }];

// Found because the reference matches
console.log(list.includes(obj)); // true

// Not found because the object reference is distinct
console.log(list.includes({ id: 1 })); // false

Sparse Arrays and Undefined Elements

When searching sparse arrays (arrays with empty slots), Array.prototype.includes() treats empty slots as having the value undefined. This allows includes() to find undefined in array holes, unlike older array iteration methods like forEach() or indexOf(), which skip empty slots entirely.

const sparseArray = [1, , 3]; // Contains an empty slot at index 1

console.log(sparseArray.includes(undefined)); // true
console.log(sparseArray.indexOf(undefined));  // -1