Docs
README
3.8 Rest Operator
Table of Contents
- ā¢What Is the Rest Operator
- ā¢Rest in Function Parameters
- ā¢Rest in Array Destructuring
- ā¢Rest in Object Destructuring
- ā¢Rest vs Spread
- ā¢Common Patterns
- ā¢Best Practices
What Is the Rest Operator
The rest operator (...) collects multiple elements into a single array or object. It's the opposite of the spread operator.
Key Concept
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā SPREAD: Expands [...array] ā element1, element2, ... ā
ā REST: Collects element1, element2, ... ā [...array] ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā function example(first, second, ...rest) { ā
ā // rest collects remaining arguments into an array ā
ā } ā
ā ā
ā example(1, 2, 3, 4, 5); ā
ā // first = 1 ā
ā // second = 2 ā
ā // rest = [3, 4, 5] ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Where Rest is Used
| Context | Example | Purpose |
|---|---|---|
| Function parameters | function fn(...args) | Collect all/remaining arguments |
| Array destructuring | const [a, ...rest] = arr | Collect remaining elements |
| Object destructuring | const {a, ...rest} = obj | Collect remaining properties |
Rest in Function Parameters
Collecting All Arguments
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2)); // 3
console.log(sum(1, 2, 3, 4, 5)); // 15
console.log(sum()); // 0
Collecting Remaining Arguments
function greet(greeting, ...names) {
return names.map((name) => `${greeting}, ${name}!`);
}
console.log(greet('Hello', 'Alice', 'Bob', 'Charlie'));
// ["Hello, Alice!", "Hello, Bob!", "Hello, Charlie!"]
Rest vs arguments Object
// Old way: arguments object (array-like, not a real array)
function oldWay() {
console.log(arguments); // { '0': 1, '1': 2, '2': 3 }
console.log(arguments.length); // 3
// arguments.map() would fail - not a real array!
return Array.from(arguments).map((x) => x * 2);
}
// New way: rest parameters (real array)
function newWay(...args) {
console.log(args); // [1, 2, 3]
return args.map((x) => x * 2); // Works directly!
}
oldWay(1, 2, 3);
newWay(1, 2, 3);
Benefits Over arguments
| Feature | arguments | Rest parameters |
|---|---|---|
| Is a real array | No | Yes |
| Works in arrow functions | No | Yes |
| Can be named | No | Yes |
| Can use array methods | No (need conversion) | Yes |
| Works with destructuring | No | Yes |
Rules for Rest Parameters
// ā
Rest must be last parameter
function valid(a, b, ...rest) {}
// ā Rest cannot be followed by other parameters
// function invalid(a, ...rest, b) { } // SyntaxError
// ā Only one rest parameter allowed
// function invalid(...a, ...b) { } // SyntaxError
// ā
Rest can be the only parameter
function onlyRest(...args) {}
Rest in Array Destructuring
Basic Usage
const numbers = [1, 2, 3, 4, 5];
const [first, second, ...rest] = numbers;
console.log(first); // 1
console.log(second); // 2
console.log(rest); // [3, 4, 5]
Skipping Elements
const values = [1, 2, 3, 4, 5, 6];
// Skip second element
const [a, , ...remaining] = values;
console.log(a); // 1
console.log(remaining); // [3, 4, 5, 6]
With Default Values
const short = [1];
const [first, second = 0, ...rest] = short;
console.log(first); // 1
console.log(second); // 0 (default)
console.log(rest); // [] (empty array)
Practical Example: Head and Tail
const list = [1, 2, 3, 4, 5];
// Common functional programming pattern
const [head, ...tail] = list;
console.log(head); // 1
console.log(tail); // [2, 3, 4, 5]
// First and last (with reverse)
const [first, ...middle] = list;
const last = list[list.length - 1];
console.log(first, middle.slice(0, -1), last); // 1, [2, 3, 4], 5
Rest in Object Destructuring
Basic Usage
const user = {
id: 1,
name: 'John',
email: 'john@example.com',
age: 30,
};
const { name, ...rest } = user;
console.log(name); // 'John'
console.log(rest); // { id: 1, email: 'john@example.com', age: 30 }
Removing Properties
const response = {
data: { users: [] },
status: 200,
headers: { 'content-type': 'application/json' },
config: { timeout: 5000 },
};
// Extract only what you need
const { data, status, ...meta } = response;
console.log(data); // { users: [] }
console.log(status); // 200
console.log(meta); // { headers: {...}, config: {...} }
Excluding Sensitive Data
const userRecord = {
id: 1,
username: 'john_doe',
password: 'hashed_password',
email: 'john@example.com',
role: 'admin',
};
// Remove password before sending to client
const { password, ...safeUser } = userRecord;
console.log(safeUser);
// { id: 1, username: 'john_doe', email: 'john@example.com', role: 'admin' }
With Renaming
const config = {
apiKey: 'abc123',
apiSecret: 'secret',
baseUrl: 'https://api.example.com',
};
const { apiKey: key, ...publicConfig } = config;
console.log(key); // 'abc123'
console.log(publicConfig); // { apiSecret: 'secret', baseUrl: '...' }
Rest vs Spread
The Same Syntax, Opposite Operations
// SPREAD: Expands into individual elements/properties
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // Spread expands arr1
// arr2 = [1, 2, 3, 4, 5]
// REST: Collects into an array/object
const [first, ...rest] = arr2; // Rest collects remaining
// first = 1, rest = [2, 3, 4, 5]
How to Tell the Difference
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā If ... is on the RIGHT side of = (or in function call) ā
ā ā It's SPREAD (expanding) ā
ā ā
ā If ... is on the LEFT side of = (or in function params) ā
ā ā It's REST (collecting) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā const copy = [...original]; // SPREAD (right side) ā
ā const [a, ...rest] = array; // REST (left side) ā
ā ā
ā func(...args); // SPREAD (in call) ā
ā function func(...args) { } // REST (in params) ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Combined Example
// REST: Collect arguments into array
function process(...items) {
// SPREAD: Expand array into new array with additions
return [...items, 'processed'];
}
const input = [1, 2, 3];
// SPREAD: Expand input into function arguments
const result = process(...input); // Same as process(1, 2, 3)
console.log(result); // [1, 2, 3, 'processed']
Common Patterns
1. Variadic Functions
// Function that accepts any number of arguments
function log(level, ...messages) {
const timestamp = new Date().toISOString();
messages.forEach((msg) => {
console.log(`[${timestamp}] [${level}] ${msg}`);
});
}
log('INFO', 'Server started', 'Port 3000');
log('ERROR', 'Connection failed');
2. Function Wrapping
function withLogging(fn) {
return function (...args) {
console.log('Arguments:', args);
const result = fn(...args);
console.log('Result:', result);
return result;
};
}
const add = (a, b) => a + b;
const loggedAdd = withLogging(add);
loggedAdd(2, 3); // Logs arguments and result
3. Array Manipulation
// Remove first element (immutably)
const removeFirst = ([, ...rest]) => rest;
console.log(removeFirst([1, 2, 3, 4])); // [2, 3, 4]
// Swap first two elements
const swapFirstTwo = ([a, b, ...rest]) => [b, a, ...rest];
console.log(swapFirstTwo([1, 2, 3, 4])); // [2, 1, 3, 4]
// Rotate array
const rotate = ([first, ...rest]) => [...rest, first];
console.log(rotate([1, 2, 3, 4])); // [2, 3, 4, 1]
4. Object Updates
// Remove property immutably
const removeProperty = (obj, prop) => {
const { [prop]: removed, ...rest } = obj;
return rest;
};
const user = { name: 'John', age: 30, email: 'john@example.com' };
console.log(removeProperty(user, 'email'));
// { name: 'John', age: 30 }
// Pick specific properties
const pick = (obj, ...keys) => {
return keys.reduce((result, key) => {
if (key in obj) result[key] = obj[key];
return result;
}, {});
};
console.log(pick(user, 'name', 'email'));
// { name: 'John', email: 'john@example.com' }
5. Partial Application
function partial(fn, ...presetArgs) {
return function (...laterArgs) {
return fn(...presetArgs, ...laterArgs);
};
}
function greet(greeting, punctuation, name) {
return `${greeting}, ${name}${punctuation}`;
}
const casualGreet = partial(greet, 'Hey', '!');
console.log(casualGreet('Alice')); // "Hey, Alice!"
const formalGreet = partial(greet, 'Good day', '.');
console.log(formalGreet('Mr. Smith')); // "Good day, Mr. Smith."
Best Practices
ā Use Rest for Flexible APIs
// Good: Flexible function signature
function createUser(name, ...options) {
const [email, age, role = 'user'] = options;
return { name, email, age, role };
}
// Can be called multiple ways
createUser('John');
createUser('John', 'john@example.com');
createUser('John', 'john@example.com', 30, 'admin');
ā Use Rest for Clean Extraction
// Good: Extract first item and keep rest
const [winner, ...runnerUps] = sortedParticipants;
// Good: Remove known properties
const { id, createdAt, ...updateData } = userInput;
ā Combine with Spread for Transformations
// Good: Transform while preserving other items
function updateFirst(fn, [first, ...rest]) {
return [fn(first), ...rest];
}
const doubled = updateFirst((x) => x * 2, [5, 10, 15]);
console.log(doubled); // [10, 10, 15]
ā Avoid Overuse
// Unnecessary rest
function bad(...args) {
const [a, b] = args; // Just use regular params
return a + b;
}
// Better
function good(a, b) {
return a + b;
}
Summary
| Context | Syntax | Purpose |
|---|---|---|
| Function params | function fn(...args) | Collect all/remaining arguments |
| Function params | function fn(a, ...rest) | Collect remaining arguments |
| Array destructuring | [a, ...rest] = arr | Collect remaining elements |
| Object destructuring | {a, ...rest} = obj | Collect remaining properties |
Key Points
- ā¢Rest operator collects elements into an array/object
- ā¢Must be the last in parameter list or destructuring
- ā¢Creates a real array (unlike
arguments) - ā¢Works with arrow functions (unlike
arguments) - ā¢Empty rest results in empty array
[]or empty object{}
Next Steps
After mastering the rest operator, proceed to:
- ā¢3.9 Nullish Coalescing - Handle null and undefined elegantly
- ā¢Practice combining rest and spread
- ā¢Build utility functions using rest parameters