Docs

3.7-Spread-Operator

3.7 Spread Operator

Table of Contents

  1. •What Is the Spread Operator
  2. •Spread with Arrays
  3. •Spread with Objects
  4. •Spread with Function Calls
  5. •Spread vs Rest
  6. •Common Use Cases
  7. •Performance Considerations

What Is the Spread Operator

The spread operator (...) allows an iterable (like an array) or an object to be expanded in places where multiple elements or properties are expected.

Syntax

// Array spread
const newArray = [...existingArray];

// Object spread
const newObject = { ...existingObject };

// Function call spread
functionName(...args);

Visual Representation

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                    SPREAD OPERATOR                          │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                             │
│  INPUT:   [1, 2, 3]    or    { a: 1, b: 2 }               │
│                ↓                    ↓                       │
│  SPREAD:   ...arr           ...obj                         │
│                ↓                    ↓                       │
│  OUTPUT:  1, 2, 3          a: 1, b: 2                      │
│           (individual     (individual                      │
│            elements)       properties)                     │
│                                                             │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Spread with Arrays

Copying Arrays

const original = [1, 2, 3];
const copy = [...original];

console.log(copy); // [1, 2, 3]
console.log(copy === original); // false (new array)

Concatenating Arrays

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];

// Using spread
const combined = [...arr1, ...arr2];
console.log(combined); // [1, 2, 3, 4, 5, 6]

// Equivalent to
const combined2 = arr1.concat(arr2);

Adding Elements

const numbers = [2, 3, 4];

// Add at beginning
const withStart = [1, ...numbers]; // [1, 2, 3, 4]

// Add at end
const withEnd = [...numbers, 5]; // [2, 3, 4, 5]

// Add at both
const withBoth = [0, ...numbers, 5]; // [0, 2, 3, 4, 5]

// Insert in middle
const withMiddle = [...numbers.slice(0, 2), 2.5, ...numbers.slice(2)];
// [2, 3, 2.5, 4]

Spreading Strings

const word = 'Hello';
const letters = [...word];
console.log(letters); // ['H', 'e', 'l', 'l', 'o']

Converting Iterables

// Set to Array
const set = new Set([1, 2, 3]);
const arrFromSet = [...set]; // [1, 2, 3]

// Map keys/values to Array
const map = new Map([
  ['a', 1],
  ['b', 2],
]);
const keys = [...map.keys()]; // ['a', 'b']
const values = [...map.values()]; // [1, 2]

// NodeList to Array (in browser)
// const divs = [...document.querySelectorAll('div')];

Shallow Copy Warning

const original = [
  [1, 2],
  [3, 4],
];
const copy = [...original];

// The inner arrays are still referenced, not copied!
copy[0].push(99);
console.log(original[0]); // [1, 2, 99] - Original affected!

Spread with Objects

Copying Objects

const original = { a: 1, b: 2 };
const copy = { ...original };

console.log(copy); // { a: 1, b: 2 }
console.log(copy === original); // false (new object)

Merging Objects

const defaults = { theme: 'light', language: 'en' };
const userPrefs = { theme: 'dark' };

const settings = { ...defaults, ...userPrefs };
console.log(settings); // { theme: 'dark', language: 'en' }
// Later spread overwrites earlier values

Adding/Overriding Properties

const user = { name: 'John', age: 30 };

// Add new property
const withEmail = { ...user, email: 'john@example.com' };
// { name: 'John', age: 30, email: 'john@example.com' }

// Override existing property
const older = { ...user, age: 31 };
// { name: 'John', age: 31 }

// Order matters!
const younger = { age: 25, ...user };
// { age: 30, name: 'John' } - user.age overwrites 25

Excluding Properties

const user = { name: 'John', password: 'secret', age: 30 };

// Using destructuring with rest to exclude
const { password, ...safeUser } = user;
console.log(safeUser); // { name: 'John', age: 30 }

Conditional Properties

const isAdmin = true;

const user = {
  name: 'John',
  ...(isAdmin && { role: 'admin' }),
  // or: ...(isAdmin ? { role: 'admin' } : {})
};

console.log(user); // { name: 'John', role: 'admin' }

Nested Object Warning

const original = {
  name: 'John',
  address: { city: 'NYC', zip: '10001' },
};

const copy = { ...original };
copy.address.city = 'LA';

console.log(original.address.city); // 'LA' - Original affected!

Spread with Function Calls

Passing Array Elements as Arguments

const numbers = [1, 5, 3, 9, 2];

// Without spread - won't work as expected
console.log(Math.max(numbers)); // NaN

// With spread - each element becomes an argument
console.log(Math.max(...numbers)); // 9
console.log(Math.min(...numbers)); // 1

Before Spread (ES5)

// Old way using apply
Math.max.apply(null, numbers); // 9

// New way with spread
Math.max(...numbers); // 9

Multiple Spreads

const arr1 = [1, 2];
const arr2 = [5, 6];

console.log(Math.max(...arr1, 3, 4, ...arr2)); // 6

With Custom Functions

function greet(first, middle, last) {
  return `Hello, ${first} ${middle} ${last}!`;
}

const nameParts = ['John', 'William', 'Doe'];
console.log(greet(...nameParts)); // "Hello, John William Doe!"

Spread vs Rest

Key Difference

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  SPREAD: Expands an array/object into individual elements  │
│  REST: Collects individual elements into an array/object   │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                             │
│  SPREAD (expanding):                                        │
│  const arr = [1, 2, 3];                                    │
│  console.log(...arr);  // 1 2 3                            │
│  const newArr = [...arr, 4];  // [1, 2, 3, 4]             │
│                                                             │
│  REST (collecting):                                         │
│  function sum(...numbers) {                                 │
│      // numbers is [1, 2, 3]                               │
│  }                                                          │
│  sum(1, 2, 3);                                             │
│                                                             │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Context Determines Behavior

// SPREAD - in array/object literals or function calls
const copy = [...original]; // Spread in array literal
const merged = { ...obj1, ...obj2 }; // Spread in object literal
func(...args); // Spread in function call

// REST - in function parameters or destructuring
function fn(...rest) {} // Rest in parameter
const [first, ...rest] = array; // Rest in destructuring
const { a, ...rest } = object; // Rest in destructuring

Common Confusion Example

// This is SPREAD - expanding array into function arguments
const nums = [1, 2, 3];
Math.max(...nums);

// This is REST - collecting arguments into array
function sum(...nums) {
  return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3);

Common Use Cases

1. Immutable Updates (React State)

// Original state
const state = {
  user: { name: 'John', age: 30 },
  items: [1, 2, 3],
};

// Update user name (immutably)
const newState = {
  ...state,
  user: { ...state.user, name: 'Jane' },
};

// Add item (immutably)
const stateWithItem = {
  ...state,
  items: [...state.items, 4],
};

2. Array Deduplication

const numbers = [1, 2, 2, 3, 3, 3, 4];
const unique = [...new Set(numbers)];
console.log(unique); // [1, 2, 3, 4]

3. Converting Arguments to Array

function legacyFunction() {
  // arguments is array-like but not an array
  const args = [...arguments];
  return args.map((x) => x * 2);
}

// Modern way: use rest parameters instead
function modernFunction(...args) {
  return args.map((x) => x * 2);
}

4. Clone and Modify

const original = { a: 1, b: 2 };

// Clone and add
const withC = { ...original, c: 3 };

// Clone and remove (using destructuring)
const { b, ...withoutB } = original;

// Clone and modify
const modified = { ...original, a: 100 };

5. Combine Arrays Conditionally

const base = ['a', 'b'];
const extra = ['c', 'd'];
const includeExtra = true;

const result = [...base, ...(includeExtra ? extra : [])];
console.log(result); // ['a', 'b', 'c', 'd']

6. Function Composition

function pipe(...fns) {
  return (value) => fns.reduce((acc, fn) => fn(acc), value);
}

const double = (x) => x * 2;
const addTen = (x) => x + 10;

const process = pipe(double, addTen);
console.log(process(5)); // 20 (5*2 + 10)

Performance Considerations

When Spread is Efficient

// Good: Small arrays/objects
const small = [...[1, 2, 3]];

// Good: One-time operations
const merged = { ...defaults, ...options };

When to Be Careful

// Careful: Very large arrays
const huge = new Array(1000000).fill(0);
const copy = [...huge]; // Creates new million-element array

// Careful: In hot loops
for (let i = 0; i < 1000; i++) {
  array = [...array, newItem]; // Bad! O(n²) complexity
}

// Better: Use push or concat for multiple additions
const items = [];
for (let i = 0; i < 1000; i++) {
  items.push(newItem); // O(n) total
}

Deep Clone Alternative

// Spread is shallow - for deep clone use:
const deepCopy = JSON.parse(JSON.stringify(original));

// Or structured clone (modern browsers)
const deepCopy = structuredClone(original);

Summary

Use CaseSpread SyntaxResult
Copy array[...arr]New array with same elements
Merge arrays[...a, ...b]Combined array
Copy object{...obj}New object with same properties
Merge objects{...a, ...b}Combined object (b overwrites a)
Function argsfn(...arr)Array elements as separate args
String to array[...str]Array of characters
Set to array[...set]Array from Set

Key Points

  • •Spread expands iterables/objects
  • •Creates shallow copies (nested objects are still referenced)
  • •Later values override earlier ones in object spread
  • •Works with any iterable for arrays
  • •Often used for immutable updates

Next Steps

After mastering the spread operator, proceed to:

  1. •3.8 Rest Operator - The counterpart that collects elements
  2. •Practice immutable state updates
  3. •Combine with destructuring for powerful patterns
.7 Spread Operator - JavaScript Tutorial | DeepML