JAVASCRIPT / DESTRUCTURING, ITERABLES, AND GENERATORS
Array destructuring and skipping positions
Pull values out of arrays and other iterables by position, skip the ones you don't need with empty slots, and collect the rest into a new array.
What you will learn
- Bind array elements by position with const [a, b] = arr
- Skip elements with empty slots, one comma per position you pass over
- Collect the tail with ...rest, which must be last and is always a real array
- Predict defaults: they fire only when the position yields undefined
Understanding Array destructuring and skipping positions
An array pattern on the left of `=` names positions instead of keys, so `const [status, body] = pair` binds `status` to the first value and `body` to the second no matter what those values are called anywhere else. Because the match is positional, the only way to reach the third value is to say something about the first two, and an empty slot is how you say "pass over this one": `const [, , third] = row`. The names you choose carry no meaning to the language; only their order does.
The pattern does not index the source. It asks the source for its iterator and pulls one value per slot, left to right, and an empty slot pulls a value and discards it. That is why commas must be counted literally rather than skimmed, and why the same syntax works on a Set or a string, which have no numeric indexes at all. It also means you can only move forward: there is no way to reach position 5 without stepping through positions 0 through 4.
Two modifiers complete the picture. A default such as `= 0` is applied when the pulled value is exactly `undefined`, which covers both slots past the end of the source and a stored `undefined`, but never `null`, `0` or `""`. A rest element gathers everything left after the named slots into a fresh array, so it has to be last, and it is `[]` rather than `undefined` when nothing remains. Patterns also nest, so `const [, [x, y]] = pairs` skips the first pair and unpacks the second.
const rgba = [255, 128, 64, 0.5];
const [red, , blue] = rgba; // the empty slot passes over 128
console.log(red, blue);
const [first, ...others] = rgba;
console.log(first, others);
const [, , , alpha = 1, extra = 'none'] = rgba; // positions 0-2 skipped
console.log(alpha, extra);
const pairs = [[1, 2], [3, 4]];
const [, [x, y]] = pairs;
console.log(x, y);An array pattern consumes the source one value at a time in order, so an empty slot is a value pulled and thrown away, not an index jumped to.
Worked examples
Skipping in a Set and a string
Shows that skipping advances an iterator rather than computing an index.
const seen = new Set(['alpha', 'beta', 'gamma', 'delta']);
const [, second, , fourth] = seen;
console.log(second, fourth);
const [, middle] = 'hey';
console.log(middle);Example explained
Line 1A Set has no numeric keys, so `seen[1]` would be undefined, yet destructuring succeeds because it walks the Set's iteration order.
Line 2Each empty slot calls the iterator once and drops the result, so `second` is the second inserted value and `fourth` is the fourth.
Line 3Strings are iterable as well, so `[, middle]` discards 'h' and binds 'e'.
Holes in parameters and callbacks
Applies a skipping pattern where the array arrives as a function argument.
function label([, name, ...tags]) {
return `${name} (${tags.join(', ')})`;
}
console.log(label(['id-7', 'widget', 'new', 'sale']));
const rows = [
['2026-01-01', 'coffee', 4.5],
['2026-01-02', 'tea', 3.25],
];
const summary = rows.map(([, item, price]) => `${item}: ${price}`);
console.log(summary);Example explained
Line 1The parameter list `([, name, ...tags])` unpacks the single argument array in place: the caller passes one array, the body sees two bindings.
Line 2The leading empty slot discards 'id-7', so `name` is 'widget' and `tags` collects positions 2 onward.
Line 3The same pattern inside `map` is the usual way to read row data whose first column is not needed.
Array holes, empty rest, and non-iterables
Covers three edge cases that decide whether a default fires or the statement throws.
const sparse = [10, , 30];
const [a = 0, b = 0, c = 0] = sparse;
console.log(a, b, c);
const [x, ...rest] = [1];
console.log(x, rest, rest.length);
try {
const [y] = null;
console.log('never runs', y);
} catch (err) {
console.log(err.constructor.name);
}Example explained
Line 1`[10, , 30]` has a hole at position 1, and the array iterator yields undefined there, so `b` falls back to 0 while `c` still gets 30.
Line 2When the source is exhausted, `rest` is a brand new empty array, not undefined, so `rest.length` is 0 and methods on it are safe.
Line 3`null` has no iterator, so the pattern throws a TypeError before `y` is created, unlike `null[0]` which would also throw but for a different reason.
Important notes
Array destructuring requires an iterable: `const [a, b] = { 0: 'x', length: 2 }` throws a TypeError even though the object looks array-like, so convert with `Array.from(...)` or `Object.values(...)` first.
A trailing comma at the very end of a pattern, as in `const [a, b,] = arr`, creates no binding and cannot change which values `a` and `b` receive, so ignore it when counting positions.
Common mistakes
Counting commas instead of positions, so `const [, , first] = arr` binds the third value; nothing throws, and the off-by-one travels quietly through the rest of the code.
Expecting a default to replace `null`: `const [port = 8080] = [null]` leaves `port` as `null`, and the fallback never runs.
Placing the rest element anywhere but last, as in `const [...head, tail] = arr` or `const [a, ...b,] = arr`, which is a SyntaxError, so the whole script fails to parse rather than failing at that line.
Try it yourself
Change, predict, then run
In a browser console start from `const log = ['GET', '/users', 200, 34];` and write one statement binding only `path` and `ms`, then a second binding `status` plus an `after` rest element. Log all four and confirm `after` is `[34]`.
Open the JavaScript workspaceCheck your understanding
Given `const nums = [1, 2, 3];` and `const [, a = 10, , b = 20] = nums;`, what do `a` and `b` hold?
- a is 2, b is 3, because empty slots are ignored when values are still available
- a is 10, b is 20, because empty slots make the neighbouring positions undefined
- a is 2, b is 20
- a is 1, b is 3, because a leading comma is treated as a no-op
Show answer
The pattern has four slots: the first consumes 1, `a` takes 2, the third consumes 3, and `b` lands on a position the array never had, so it is undefined and the default 20 applies. The tempting answer is a is 2 and b is 3, but that assumes an empty slot is skipped over rather than consuming a value; the slot between `a` and `b` really does pull 3 and discard it.