Skip to main content

Menu

System onlineAI Tutorials / 2026

AI Tutorials
Knowledge search

Recent · Popular · Tutorials

  • dart3 tutorials
  • java1 tutorial
  • Javascript3 tutorials
  • Node.js Introduction2 tutorials
  • dartdart · General · In depth
  • JavascriptJavascript · Advanced · In depth
  • JavascriptJavascript · Advanced · Mid
  • JavascriptJavascript · Advanced · Short

Find a tutorial

Tell the assistant what you want to learn. It suggests published chapters.

For example: “I want to learn Rust ownership from the start.” The conversation is kept only in this tab.

0 / 2,000 characters

Enter to send, Shift+Enter for a new line. Do not include personal information.

Chapter 9

Destructuring and Spread

**Prerequisites assumed:** You are comfortable with objects, arrays, iteration, `let`/`const`, rest parameters, and the iterator protocol.
Level: AdvancedLength: Mid2 min read

Read at another depth

Destructuring and Spread

Prerequisites assumed: You are comfortable with objects, arrays, iteration, let/const, rest parameters, and the iterator protocol.

The mechanics

Destructuring is pattern matching against iterables and objects. The right-hand side is destructured; the left-hand side is the pattern. Object patterns bind properties by name; array patterns consume an iterable positionally, calling Symbol.iterator internally — so any iterable destructures, not just arrays.

Code example in javascript
const { name, ...rest } = { name: 'Ada', role: 'dev', level: 5 };
// name: 'Ada'; rest: { role: 'dev', level: 5 } — a fresh plain object

const [first, , third = 'n/a'] = [1, 2]; // holes skip; third hits its default

Defaults only apply to undefined, never null or other falsy values. const { x = 1 } = { x: null } yields x === null. For true "absent or null" handling you need explicit fallbacks, e.g. x ?? 1 in a subsequent expression.

Nested patterns and renaming

Patterns nest arbitrarily, and the rename syntax is { source: target, nested: { a, b } }. A subtle gotcha: in const { port: p = 8080 }, the default belongs to p, not port. Assigning a default to a renamed binding looks like { port: p = 8080 } — reading it wrong is a common review bug.

Code example in javascript
const response = { data: { user: { id: 7, tags: ['admin'] } } };
const {
  data: {
    user: { id, tags: [primary] },
  },
} = response; // id: 7, primary: 'admin'
// Note: `data` and `user` are NOT bound here — they were paths, not targets.

Spread: copy, merge, and its limits

Spread at the call site or in literals copies own enumerable properties/elements one level deep. It invokes the iterator (arrays, strings, Maps, Sets) or CopyDataProperties (objects). Consequences:

  • Spread of a string yields an array of code points, not UTF-16 code units — emoji spread correctly where str.split('') does not.
  • Object spread runs getters on the source during the copy and silently drops non-enumerable and symbol-keyed... actually it does copy own enumerable symbols; it drops non-enumerables and prototype members.
  • Both { ...a, ...b } and Object.assign({}, a, b) shallow-merge; neither deep-clones. Nested references are shared — a classic source of accidental mutation across "copies".
Code example in javascript
const base = { opts: { deep: true } };
const copy = { ...base };
copy.opts.deep = false;
console.log(base.opts.deep); // false — shared reference

Rest, parameters, and evaluation order

Rest elements must be last in array patterns and parameters; trailing commas after rest are a syntax error. In function signatures, rest parameters capture leftovers (always a real array), and defaults are evaluated left to right — earlier parameters can seed later ones:

Code example in javascript
function area({ w, h = w * 2, unit = 'px' } = {}) {
  return `${w}x${h} ${unit}`;
}
Flow from source value through iterator or property read, binding targets, applying defaults when undefined.
100%
Destructuring pattern resolution
Show diagram source
flowchart LR
  A[Source value] --> B{Array pattern?}
  B -- yes --> C[Get iterator, consume positions]
  B -- no --> D[Read properties by name]
  C --> E{Value undefined?}
  D --> E
  E -- yes --> F[Use default / rest]
  E -- no --> G[Bind target]
  F --> G

Exercise

Why does const [a, b] = 'hi😀' give a = 'h', b = 'i' with the emoji lost, and how do you fix it?

Show answer

Array patterns use the iterator, which walks code points — but with only two bindings, the emoji (a full code point pair 'i' + '😀'... actually 'i' and '😀' are two code points) is simply never bound. Fix it by binding all code points ([...str]) and slicing, or accept that the pattern stops after two positions. The point: destructuring consumes lazily by position; the emoji was at position 3, never requested.

Exercise

In { ...base, opts: { ...base.opts, deep: false } }, why is base.opts.deep unaffected?

Show answer

The nested spread creates a new opts object before mutation, breaking the shared reference. Shallow copying each level you intend to mutate is the standard workaround for the one-level-deep limit.

Spotted a mistake, or code that does not work?

Report an issue

Chapter: Destructuring and Spread

0 / 2,000 characters

Plain text, for example: the second code sample does not compile. Do not include personal information.

You can't do that