Destructuring and spread operators are among the most practical ES6 features. This article covers their various uses and techniques.
Array Destructuring
Basic Usage
// Basic destructuring
const [a, b, c] = [1, 2, 3];
console.log(a, b, c); // 1 2 3
// Skip elements
const [first, , third] = [1, 2, 3];
console.log(first, third); // 1 3
// Rest elements
const [head, ...tail] = [1, 2, 3, 4, 5];
console.log(head); // 1
console.log(tail); // [2, 3, 4, 5]
// Default values
const [x = 10, y = 20] = [1];
console.log(x, y); // 1 20
// Nested destructuring
const [a1, [b1, b2], c1] = [1, [2, 3], 4];
console.log(a1, b1, b2, c1); // 1 2 3 4
Practical Techniques
// Swap variables
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b); // 2 1
// Function returning multiple values
function getMinMax(arr) {
return [Math.min(...arr), Math.max(...arr)];
}
const [min, max] = getMinMax([3, 1, 4, 1, 5]);
console.log(min, max); // 1 5
// Destructuring function parameters
function processArray([first, second, ...rest]) {
console.log(first); // 1
console.log(second); // 2
console.log(rest); // [3, 4, 5]
}
processArray([1, 2, 3, 4, 5]);
// With regular expressions
const url = 'https://example.com:8080/path';
const [, protocol, host, port] = url.match(/^(\w+):\/\/([^:]+):(\d+)/);
console.log(protocol, host, port); // 'https' 'example.com' '8080'
Object Destructuring
Basic Usage
// Basic destructuring
const user = { name: 'Alice', age: 25 };
const { name, age } = user;
console.log(name, age); // 'Alice' 25
// Renaming
const { name: userName, age: userAge } = user;
console.log(userName, userAge); // 'Alice' 25
// Default values
const { name: n, city = 'Unknown' } = user;
console.log(n, city); // 'Alice' 'Unknown'
// Rename + default value
const { name: n2, city: c = 'NYC' } = user;
console.log(n2, c); // 'Alice' 'NYC'
// Nested destructuring
const person = {
name: 'Bob',
address: {
city: 'Tokyo',
country: 'Japan'
}
};
const { address: { city, country } } = person;
console.log(city, country); // 'Tokyo' 'Japan'
Rest Properties
// Extract some properties, collect the rest
const user = {
id: 1,
name: 'Alice',
age: 25,
email: 'alice@example.com'
};
const { id, ...rest } = user;
console.log(id); // 1
console.log(rest); // { name: 'Alice', age: 25, email: '...' }
// Practical: Remove object property
function removeField(obj, field) {
const { [field]: _, ...rest } = obj;
return rest;
}
removeField(user, 'email');
// { id: 1, name: 'Alice', age: 25 }
Function Parameter Destructuring
// Destructure parameters
function greet({ name, age }) {
console.log(`Hello ${name}, you are ${age}`);
}
greet({ name: 'Alice', age: 25 });
// With default values
function createUser({
name = 'Anonymous',
age = 0,
role = 'user'
} = {}) {
return { name, age, role };
}
createUser(); // { name: 'Anonymous', age: 0, role: 'user' }
createUser({ name: 'Bob' }); // { name: 'Bob', age: 0, role: 'user' }
// Complex default values
function configure({
host = 'localhost',
port = 3000,
ssl: {
enabled = false,
cert = null
} = {}
} = {}) {
return { host, port, ssl: { enabled, cert } };
}
Spread Operator
Array Spread
// Merge arrays
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const merged = [...arr1, ...arr2];
// [1, 2, 3, 4, 5, 6]
// Copy array (shallow copy)
const original = [1, 2, 3];
const copy = [...original];
// Insert in middle
const inserted = [...arr1.slice(0, 2), 'new', ...arr1.slice(2)];
// [1, 2, 'new', 3]
// Convert iterables
const str = 'hello';
const chars = [...str]; // ['h', 'e', 'l', 'l', 'o']
const set = new Set([1, 2, 3]);
const arrFromSet = [...set]; // [1, 2, 3]
// Function calls
const numbers = [1, 5, 3, 9, 2];
Math.max(...numbers); // 9
Object Spread
// Merge objects
const defaults = { host: 'localhost', port: 3000 };
const options = { port: 8080, ssl: true };
const config = { ...defaults, ...options };
// { host: 'localhost', port: 8080, ssl: true }
// Copy object (shallow copy)
const original = { a: 1, b: 2 };
const copy = { ...original };
// Add/override properties
const user = { name: 'Alice', age: 25 };
const updatedUser = { ...user, age: 26, city: 'NYC' };
// { name: 'Alice', age: 26, city: 'NYC' }
// Conditional spread
const condition = true;
const obj = {
always: true,
...(condition && { sometimes: true })
};
// { always: true, sometimes: true }
// Or use ternary
const obj2 = {
...baseObj,
...(isAdmin ? adminProps : userProps)
};
Rest Parameters
Function Parameters
// Collect all arguments
function sum(...numbers) {
return numbers.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3, 4, 5); // 15
// Combine with fixed parameters
function log(level, ...messages) {
console.log(`[${level}]`, ...messages);
}
log('INFO', 'User logged in', 'at 10:00');
// [INFO] User logged in at 10:00
// Replace arguments object
// Old way
function oldWay() {
const args = Array.prototype.slice.call(arguments);
return args.join(' ');
}
// New way (recommended)
function newWay(...args) {
return args.join(' ');
}
Rest in Destructuring
// Array destructuring
const [first, second, ...others] = [1, 2, 3, 4, 5];
console.log(others); // [3, 4, 5]
// Object destructuring
const { a, b, ...remaining } = { a: 1, b: 2, c: 3, d: 4 };
console.log(remaining); // { c: 3, d: 4 }
Advanced Patterns
Dynamic Property Destructuring
const key = 'name';
const obj = { name: 'Alice', age: 25 };
// Computed property name destructuring
const { [key]: value } = obj;
console.log(value); // 'Alice'
// Utility function
function pick(obj, ...keys) {
return keys.reduce((result, key) => {
if (key in obj) {
result[key] = obj[key];
}
return result;
}, {});
}
pick(obj, 'name'); // { name: 'Alice' }
Deep Merge
// Shallow merge (spread operator)
const merged = { ...obj1, ...obj2 }; // Nested objects are overwritten
// Deep merge function
function deepMerge(target, source) {
const result = { ...target };
for (const key of Object.keys(source)) {
if (
source[key] !== null &&
typeof source[key] === 'object' &&
!Array.isArray(source[key]) &&
target[key] !== null &&
typeof target[key] === 'object'
) {
result[key] = deepMerge(target[key], source[key]);
} else {
result[key] = source[key];
}
}
return result;
}
const obj1 = { a: 1, nested: { x: 1, y: 2 } };
const obj2 = { b: 2, nested: { y: 3, z: 4 } };
deepMerge(obj1, obj2);
// { a: 1, b: 2, nested: { x: 1, y: 3, z: 4 } }
React Applications
// Props passing
function ParentComponent() {
const commonProps = { className: 'common', onClick: handleClick };
return <ChildComponent {...commonProps} id="child1" />;
}
// State updates
const [state, setState] = useState({ name: '', age: 0 });
setState(prev => ({ ...prev, name: 'Alice' }));
// Destructure props
function UserCard({ name, age, ...rest }) {
return (
<div {...rest}>
<h2>{name}</h2>
<p>Age: {age}</p>
</div>
);
}
Best Practices Summary
Destructuring and Spread Best Practices:
┌─────────────────────────────────────────────────────┐
│ │
│ Destructuring Usage │
│ ├── Extract needed properties for cleaner code │
│ ├── Use in function params for readability │
│ ├── Provide sensible default values │
│ └── Avoid deeply nested destructuring │
│ │
│ Spread Usage │
│ ├── Keep immutability when merging objects/arrays│
│ ├── Remember it's a shallow copy │
│ ├── Use && or ternary for conditional spread │
│ └── Later properties override earlier ones │
│ │
│ Performance Considerations │
│ ├── Large object spreads have performance cost │
│ ├── Consider alternatives for frequent spreads │
│ └── Deep nesting requires deep merge │
│ │
└─────────────────────────────────────────────────────┘
| Operation | Syntax | Purpose |
|---|---|---|
| Array destructuring | [a, b] = arr | Extract array elements |
| Object destructuring | {a, b} = obj | Extract object properties |
| Array spread | [...arr] | Merge/copy arrays |
| Object spread | {...obj} | Merge/copy objects |
| Rest parameters | ...rest | Collect remaining elements |
Master destructuring and spread for more concise and elegant code.