Destructuring
Extracts values by position.
🎯 What you'll learn
- Array Destructuring
- Object Destructuring
- Nested Destructuring
- Destructuring Function Parameters
- Swapping Variables
1Array Destructuring
Extracts values by position.
const [first, second] = [10, 20, 30]; // first=10, second=20 (30 is ignored)
const [, , third] = [10, 20, 30]; // skip positions with empty commas -> third=30
const [head, ...tail] = [1, 2, 3, 4]; // head=1, tail=[2,3,4]
const [a = 100, b = 200] = [undefined, 5]; // a=100 (default used), b=52Object Destructuring
Extracts values by key name, order-independent.
const user = { name: "Ana", age: 30, city: "Berlin" };
const { name, age } = user; // name="Ana", age=30
const { name: userName } = user; // rename while destructuring -> userName="Ana"
const { role = "guest" } = user; // role="guest" (default, key doesn't exist)
const { city: userCity = "Unknown" } = user; // rename + default combined3Nested Destructuring
const response = {
data: { user: { id: 1, profile: { avatar: "url.png" } } },
status: 200,
};
const {
status,
data: {
user: { id, profile: { avatar } },
},
} = response;
// status=200, id=1, avatar="url.png"
// NOTE: `data` and `user` themselves are NOT created as variables here —
// only the leaf names actually pulled out (status, id, avatar) become bindings.4Destructuring Function Parameters
function createUser({ name, age = 18, roles = [] }) {
return { name, age, roles };
}
createUser({ name: "Ana" }); // { name: "Ana", age: 18, roles: [] }
function move({ x, y } = { x: 0, y: 0 }) { // default for the WHOLE object if none is passed
return [x, y];
}
move(); // [0, 0]This is the standard pattern for "named arguments" / options objects in JavaScript, replacing long positional parameter lists.
5Swapping Variables
let a = 1, b = 2;
[a, b] = [b, a]; // a=2, b=1 — no temporary variable needed6Destructuring in Loops
const entries = [["a", 1], ["b", 2]];
for (const [key, value] of entries) {
console.log(key, value);
}
const users = [{ id: 1, name: "Ana" }, { id: 2, name: "Leo" }];
for (const { id, name } of users) {
console.log(id, name);
}7Combining with Rest
const { id, ...rest } = { id: 1, name: "Ana", age: 30 };
// id=1, rest={ name: "Ana", age: 30 }
const [first, ...others] = [1, 2, 3];
// first=1, others=[2, 3]Rest must always come last in both array and object destructuring patterns.
Under the HoodDestructuring Is Pure Syntax, Not a Runtime Feature▾
Destructuring adds no new capability to the engine — it's parsed and compiled down to exactly the sequential property/index accesses you'd write by hand. There is no special "destructuring engine" doing anything smarter than that.
const { name, age } = user;
// compiles down to roughly:
const name = user.name;
const age = user.age;const [first, second] = arr;
// compiles down to roughly:
const first = arr[0];
const second = arr[1];This means nested destructuring costs exactly what manual chaining costs
const { data: { user: { profile: { avatar } } } } = response;
// costs exactly the same as:
const avatar = response.data.user.profile.avatar;Every one of those accesses is a real property lookup, subject to the exact same hidden-class / inline-cache machinery from the Prototypes and Prototype Chain topics — destructuring doesn't skip that machinery or make deeply nested access any cheaper than writing it out by hand. Its entire value is readability at the source level, with zero runtime cost difference either way.
Destructuring a primitive triggers temporary auto-boxing
const { length } = "hello"; // length = 5Strings aren't objects, but .length is a property access — the engine temporarily wraps the primitive in its corresponding wrapper object (String, Number, Boolean) just long enough to perform the lookup, then discards the wrapper. This is the same auto-boxing mechanism that lets "hello".toUpperCase() work despite strings being primitives — destructuring a primitive simply routes through the same mechanism section 1's array/object examples don't need, since arrays and objects are already real objects.
Why destructuring null/undefined throws instead of silently failing
const { a } = null; // TypeError: Cannot destructure property 'a' of 'null' as it is null.Property access fundamentally requires a real object (or something auto-boxable) to read from — null/undefined have no properties and can't be boxed into anything, so the underlying property-read operation fails exactly the way null.a would fail if you wrote it manually. This is why the common fallback pattern function f({ a } = {}) works: it doesn't make destructuring "safer" in general, it just ensures the value being destructured is never null/undefined in the first place.
⚠️ Common Pitfalls
- Destructuring
undefined/nulldirectly throws —const { a } = undefined;is aTypeError, nota = undefined. Always ensure a fallback object (= {}) when the source might be missing. - Renaming syntax direction confusion —
{ name: userName }reads "takename, call ituserName" — the source key is on the left of the colon, the new local name on the right. Easy to reverse by mistake. - Defaults only trigger on
undefined, exactly like function default parameters —nulldoes not trigger a destructuring default. - Forgetting nested destructuring doesn't create intermediate variables — only the innermost names you explicitly write become bindings.
- Destructuring in a function signature swallowing
undefined— callingcreateUser()with no argument at all throws unless the whole parameter has a default:function createUser({ name } = {}).
📋 Quick Reference
[a, b] = arr → positional
{a, b} = obj → by key
{a: renamed} = obj → rename
{a = default} = obj → default if undefined
{a, ...rest} = obj → rest must be last🎤 Interview Q&A
Q1How does object and array destructuring work?core▾
Extract values into variables: const { id, name: title = 'Untitled', ...rest } = obj (rename, default, rest) and const [first, , third] = arr (positions, skipping). Works in parameters too.
- When do defaults apply? Only when the value is
undefined, notnull.
Q2How do you swap variables with destructuring?core▾
[a, b] = [b, a];
- Performance? Fine for normal code; creates a temporary array.
Q3How do you destructure nested objects safely?core▾
const { address: { city } = {} } = user; provides a default object to avoid errors when address is undefined; optional chaining (user.address?.city) is often clearer.
- What error occurs otherwise? TypeError: cannot destructure property of undefined.
Q4How is destructuring used in function parameters?senior▾
function createUser({ name, role = 'user' } = {}) {} gives named, optional arguments with defaults; React components destructure props this way.
- Why
= {}? So calling without arguments doesn't throw.
Q5What does rest in object destructuring do?core▾
const { password, ...safeUser } = user collects remaining own enumerable properties into a new object, a handy way to omit fields.
- Is it a deep copy? No, shallow.
🛠️ Practice exercises
Each exercise is a file in this topic folder. Code exercises are self-checking: run them and make the checks pass.