Array.prototype.keys()
Returns an iterator over every index of the array, including empty slots.
keys()- Added in JavaScript ECMAScript 2015
Return value
Array IteratorA new iterable iterator object yielding each integer index from 0 to length - 1.
Errors
Throws a TypeError only if called on null or undefined.
keys() yields the numbers 0 through length - 1 as an iterator. It is the index half of the trio with values() and entries().
When to reach for it
Two situations. First, iterating indexes with for...of and break support, which forEach() does not offer. Second — and more common in practice — generating a range: [...Array(5).keys()] is [0, 1, 2, 3, 4], and it works because keys() is driven by length rather than by which indexes actually exist.
What trips people up
- It is not
Object.keys().Object.keys()returns string keys and skips holes;keys()returns numbers and includes every index in range. For["a", , "c"]you get["0", "2"]versus[0, 1, 2]. - The iterator is single-use. Once exhausted, a second
for...ofover the same iterator yields nothing. - It reads
lengthon each step, so appending during iteration extends the loop and truncating ends it early. - If you need the index and the value,
entries()saves a lookup.
Example
When to use it
- Generate a numeric range with [...Array(n).keys()].
- Loop over indexes with for...of so you can break or continue.
- Walk every index of a sparse array, holes included.
- Build a list of positions to feed into another lookup table.
Frequently asked questions
How do I create a range of numbers in JavaScript?
Use [...Array(n).keys()] to get 0 through n-1 as an array. Array.from({length: n}, (_, i) => i + start) is the version to reach for when the range does not start at zero.
What is the difference between Array.prototype.keys() and Object.keys()?
Array.prototype.keys() returns an iterator of numeric indexes covering the full length, holes included, while Object.keys() returns an array of string property names and skips empty slots. They disagree on any sparse array.
Can I reuse the iterator returned by keys()?
No, it is a one-time object: once it is exhausted, further iteration yields nothing. Call keys() again for a fresh iterator, or spread it into an array if you need to traverse the indexes more than once.
More examples
Ranges without a loop
Array(n) creates n empty slots, and keys() still yields an index for each because it only consults length. The Array.from version produces the same output and is the clearer choice when you also want to transform each index.
const range = (n) => [...Array(n).keys()];
console.log(range(6));
// Shifted or scaled ranges
console.log(range(5).map((i) => i + 1));
console.log(range(4).map((i) => i * 10));
// Equivalent with Array.from
console.log(Array.from({ length: 4 }, (_, i) => i));The iterator is consumed once
The first next() takes index 0, the spread drains the remaining 1 and 2, and the second spread finds nothing left. An exhausted iterator keeps returning done: true forever.
const it = ["x", "y", "z"].keys();
console.log(it.next());
console.log([...it]);
console.log([...it]);
console.log(it.next());Breaking out of an index loop
for...of over keys() supports break, so the loop stops at index 2 and never inspects the last two elements. forEach() would have to run to completion.
const data = [4, 8, 15, 16, 23];
for (const i of data.keys()) {
if (data[i] > 14) {
console.log("first big value at index", i);
break;
}
console.log("checked index", i);
}
Discussion