Array.prototype.indexOf()

Strict equality throughout, which has one famous consequence: an array containing NaN still reports -1 when you search for NaN.

Array methodES5 (2009)Live demo
Common call
items.indexOf(value)
Returns
number — the index, or -1 when absent
Replaces
a manual loop comparing each element
Watch out
-1 is truthy, so `if (indexOf(x))` is almost always a bug
array.indexOf(searchElement[, fromIndex])
→ number

Demo

Live evaluation
Try:
Inputs
itemsnumber[]numbers, comma separated
valuenumbervalue to locate
Output
[1, 2, 3].indexOf(2)
1

The first matching index comes back, or -1 when there is no match. With duplicates you always get the earliest. The -1 is the part to be careful with: it is a perfectly ordinary number and it is TRUTHY, so testing the result without comparing it explicitly gives the opposite of what you meant for an element at index 0.

Parameters

NameTypeRequiredDescription
searchElementanyyesValue to locate, compared with strict equality (===).
fromIndexnumberno (0)Index to start from. Negative counts from the end. Past the length returns -1 immediately.

Return value

number — Index of the first strictly-equal element, or -1 if there is none. NaN is never found, because NaN !== NaN.

Common patterns

Find then remove
The usual pairing with splice.
const i = items.indexOf(value);
if (i !== -1) items.splice(i, 1);
Prefer includes for presence
When you do not need the position.
if (items.includes(value)) { ... }
Find every occurrence
fromIndex advances past each hit.
let i = items.indexOf(v);
while (i !== -1) {
  found.push(i);
  i = items.indexOf(v, i + 1);
}

Examples

1. Found
[1, 2, 3].indexOf(2)
Returns
1
2. Absent
[1, 2, 3].indexOf(99)
Returns
-1
3. First of two
[1, 2, 1].indexOf(1)
Returns
0
4. NaN never found
[NaN].indexOf(NaN)
Returns
-1
5. includes can
[NaN].includes(NaN)
Returns
true
6. Holes not found
[1, , 3].indexOf(undefined)
Returns
-1

Pitfalls

1. -1 is truthy
The classic. Testing the result directly is backwards: a missing element gives -1 which is truthy, and an element at index 0 gives 0 which is falsy. Both cases come out exactly wrong.
Backwards
if (items.indexOf(x)) { /* "found" */ }
true when ABSENT, false at index 0
Compare explicitly
if (items.indexOf(x) !== -1) { ... }
correct
2. It can never find NaN
indexOf compares with ===, and NaN is not equal to itself by definition. An array visibly containing NaN still reports -1 — which is precisely why includes was added.
Not found
[NaN].indexOf(NaN)
-1
Use includes
[NaN].includes(NaN)
true
3. No type coercion
Strict equality means a numeric string never matches a number. Values from forms, query strings and JSON are the usual culprits.
String vs number
[1, 2, 3].indexOf('2')
-1
Convert first
[1, 2, 3].indexOf(Number("2"))
1
4. Objects match by identity
A structurally identical object is a different value, so indexOf returns -1. Use findIndex with a predicate when you need to match on contents.
Different object
[{id: 1}].indexOf({id: 1})
-1
Use findIndex
[{id: 1}].findIndex(o => o.id === 1)
0

When to use

Use it
  • You need the POSITION of a value
  • Find-then-splice removal by value
  • Walking every occurrence with fromIndex
Reach for something else
  • You only need presence → includes, which reads better
  • The value might be NaN → includes
  • Matching objects by contents → findIndex

Notes

Complexity
O(n) — a linear scan, stopping at the first match
Return
A number; -1 means absent, and -1 is truthy
CPython impl
V8: Builtins-array-indexof.tq
Memory
No allocation
Thread-safe
Single-threaded; the source is only read

FAQ

Because it compares with strict equality, and NaN === NaN is false by IEEE-754 definition. No strict-equality search can ever match NaN. includes uses SameValueZero instead, which treats NaN as equal to itself.

[NaN].indexOf(NaN)    // -1
[NaN].includes(NaN)   // true

History

ES5
indexOf standardised in 2009, alongside lastIndexOf.
ES2016
includes added, giving a membership test that handles NaN.