Skip to main content

JavaScript Rest Spread

JavaScript's rest and spread operators are powerful features introduced in ES6 (ECMAScript 2015) that make working with arrays and objects much more convenient. Both use the same ... syntax but serve different purposes depending on the context.

Introduction

The rest and spread operators might look identical (...), but they do opposite things:

  • Spread operator: Expands elements from an array or properties from an object
  • Rest operator: Collects multiple elements into an array or object properties

Let's explore both features and see how they can make your JavaScript code more elegant and expressive.

The Spread Operator

The spread operator (...) allows you to expand an iterable (like an array) into individual elements, or an object into individual key-value pairs.

Spreading Arrays

Basic Array Spreading

javascript
const numbers = [1, 2, 3];
const newNumbers = [...numbers, 4, 5];

console.log(newNumbers); // Output: [1, 2, 3, 4, 5]

In this example, we've expanded the numbers array and added more elements to create a new array.

Combining Arrays

javascript
const fruits = ['apple', 'banana'];
const vegetables = ['carrot', 'tomato'];
const produce = [...fruits, ...vegetables];

console.log(produce); // Output: ['apple', 'banana', 'carrot', 'tomato']

Copying Arrays

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

copy.push(4);
console.log(original); // Output: [1, 2, 3]
console.log(copy); // Output: [1, 2, 3, 4]

This creates a shallow copy of the original array.

Spreading Objects

Copying and Adding Properties

javascript
const person = { name: 'John', age: 30 };
const extendedPerson = {
...person,
job: 'Developer',
location: 'New York'
};

console.log(extendedPerson);
// Output: { name: 'John', age: 30, job: 'Developer', location: 'New York' }

Merging Objects

javascript
const defaults = { theme: 'dark', fontSize: 16 };
const userSettings = { fontSize: 20 };
const settings = { ...defaults, ...userSettings };

console.log(settings);
// Output: { theme: 'dark', fontSize: 20 }

Note that when properties overlap, the last object's properties will override earlier ones.

Function Arguments

You can use the spread operator to pass array elements as separate arguments:

javascript
function sum(x, y, z) {
return x + y + z;
}

const numbers = [1, 2, 3];
console.log(sum(...numbers)); // Output: 6

The Rest Operator

The rest operator (...) collects multiple elements into an array or object. It's essentially the opposite of the spread operator.

Rest with Arrays

Collecting Remaining Elements

javascript
const [first, second, ...rest] = [1, 2, 3, 4, 5];

console.log(first); // Output: 1
console.log(second); // Output: 2
console.log(rest); // Output: [3, 4, 5]

Rest with Function Parameters

The rest operator allows you to represent an indefinite number of arguments as an array:

javascript
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}

console.log(sum(1, 2)); // Output: 3
console.log(sum(1, 2, 3, 4)); // Output: 10

Combining Regular Parameters with Rest

javascript
function displayInfo(name, age, ...hobbies) {
console.log(`Name: ${name}`);
console.log(`Age: ${age}`);
console.log(`Hobbies: ${hobbies.join(', ')}`);
}

displayInfo('Jane', 25, 'reading', 'swimming', 'coding');
// Output:
// Name: Jane
// Age: 25
// Hobbies: reading, swimming, coding

Rest with Objects

You can also use the rest operator with object destructuring:

javascript
const { name, age, ...rest } = {
name: 'Alice',
age: 28,
job: 'Engineer',
location: 'London',
hobbies: ['reading', 'hiking']
};

console.log(name); // Output: Alice
console.log(age); // Output: 28
console.log(rest); // Output: { job: 'Engineer', location: 'London', hobbies: ['reading', 'hiking'] }

Real-World Applications

Function with Default Options

javascript
function createUser(username, { age = 18, role = 'user', ...otherInfo } = {}) {
return {
username,
age,
role,
...otherInfo,
createdAt: new Date()
};
}

const user1 = createUser('john_doe', { age: 25, role: 'admin', email: '[email protected]' });
console.log(user1);
// Output:
// {
// username: 'john_doe',
// age: 25,
// role: 'admin',
// email: '[email protected]',
// createdAt: [Date object]
// }

const user2 = createUser('jane_doe');
console.log(user2);
// Output:
// {
// username: 'jane_doe',
// age: 18,
// role: 'user',
// createdAt: [Date object]
// }

State Management in React

While this is a React example, it illustrates a common use case for the spread operator:

javascript
// Updating state in React
function reducer(state, action) {
switch (action.type) {
case 'UPDATE_USER':
return {
...state,
user: {
...state.user,
...action.payload
}
};
// other cases...
default:
return state;
}
}

Dynamic Function Arguments

javascript
function logData(method, ...data) {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] [${method}]:`, ...data);
}

logData('INFO', 'User logged in', { userId: 123 });
// Output: [2023-09-15T12:00:00.000Z] [INFO]: User logged in { userId: 123 }

Common Gotchas and Tips

Rest Must Be the Last Parameter

When using rest parameters in function definitions, the rest parameter must be the last parameter:

javascript
// ✅ Correct
function correct(a, b, ...rest) {}

// ❌ Incorrect - will cause a SyntaxError
// function incorrect(...rest, a, b) {}

Spread Creates Shallow Copies

The spread operator only creates a shallow copy, which means that nested objects or arrays are still references to the original:

javascript
const original = { 
name: 'John',
skills: ['JavaScript', 'React']
};

const copy = { ...original };
copy.skills.push('Node.js');

console.log(original.skills); // Output: ['JavaScript', 'React', 'Node.js']
console.log(copy.skills); // Output: ['JavaScript', 'React', 'Node.js']

To create a deep copy, you'd need to handle nested structures manually or use libraries like lodash's cloneDeep.

Summary

The rest and spread operators are powerful features in modern JavaScript:

  • Spread (...) expands iterables like arrays into individual elements or objects into individual properties
  • Rest (...) collects multiple elements into an array or multiple properties into an object

These operators help you write more concise and expressive code, particularly when:

  • Creating copies of arrays and objects
  • Combining arrays or objects
  • Working with variable numbers of function parameters
  • Destructuring arrays and objects
  • Managing state in applications

Exercises

  1. Create a function that takes any number of arguments and returns their sum.
  2. Write a function that merges two objects, but if there are conflicts, values from the second object should be arrays containing both values.
  3. Create a function that clones an array of objects deeply (not a shallow copy).

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)