← JS Mastery | Module 4: Objects & Arrays Spread Operator & Rest Parameters
Module 4

Spread Operator & Rest Parameters

⏱ 16 min read ● Intermediate 🆓 Free

Spread Operator (...)

The spread operator (...) expands an iterable (array, string, object) into individual elements. It looks like rest but does the opposite — spread expands, rest collects.

// Spread arrays:
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2]; // [1,2,3,4,5,6]
const withExtra = [0, ...arr1, ...arr2, 7]; // [0,1,2,3,4,5,6,7]

// Spread creates a SHALLOW copy:
const original = [1, 2, 3];
const copy = [...original];
copy.push(99);
console.log(original); // [1,2,3] — unchanged

// Spread into function arguments:
const numbers = [3, 1, 4, 1, 5, 9, 2];
console.log(Math.max(...numbers)); // 9
console.log(Math.min(...numbers)); // 1

// Spread objects:
const defaults = { theme: "dark", lang: "en", size: 14 };
const custom = { lang: "fr", size: 16 };
const merged = { ...defaults, ...custom };
// { theme: "dark", lang: "fr", size: 16 }
// Later properties override earlier ones

// Shallow copy an object:
const user = { name: "Alice", address: { city: "NYC" } };
const userCopy = { ...user };
userCopy.name = "Bob";           // doesn't affect original
userCopy.address.city = "LA";    // DOES affect original! (shallow copy)

// Deep clone needs structuredClone():
const deepCopy = structuredClone(user);

Rest Parameters

// Rest collects remaining elements into an array
function sum(...numbers) {
  return numbers.reduce((acc, n) => acc + n, 0);
}
console.log(sum(1, 2, 3, 4, 5));   // 15

// Mix with regular params — rest must be last!
function logMessage(level, ...messages) {
  const prefix = `[${level.toUpperCase()}]`;
  console.log(prefix, ...messages);
}
logMessage("info", "Server", "started", "on port 3000");

// Rest in destructuring:
const [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(first);  // 1
console.log(second); // 2
console.log(rest);   // [3,4,5]

const { name, ...otherProps } = { name: "Alice", age: 30, city: "NYC" };
console.log(name);        // "Alice"
console.log(otherProps);  // { age: 30, city: "NYC" }
// This is useful for "omitting" a property from an object!

Practical Patterns

// Remove a property from object (non-mutating):
const { password, ...safeUser } = userWithPassword;
// safeUser doesn't have password — safe to log/return

// Merge with override:
function updateUser(user, updates) {
  return { ...user, ...updates, updatedAt: new Date() };
}

// Clone and add property:
const withId = { ...newItem, id: generateId() };

// Function that accepts any number of items:
const addToCart = (...items) => {
  return items.map(item => ({ ...item, addedAt: Date.now() }));
};

// Spread string:
const chars = [..."hello"];  // ["h","e","l","l","o"]
const unique = [...new Set("mississippi")]; // ["m","i","s","p"]

⚡ Key Takeaways

🎯 Practice Exercises

EXERCISE 1

Write a mergeDefaults(config) function that takes a partial config and returns it merged with sensible defaults for missing properties.

EXERCISE 2

Implement an omit(obj, ...keys) function that returns a new object without the specified keys (use rest and spread destructuring).

← Destructuring