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: 'john@example.com' });
console.log(user1);
// Output:
// {
// username: 'john_doe',
// age: 25,
// role: 'admin',
// email: 'john@example.com',
// 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 feedback@compilenrun.com. I’d greatly appreciate your feedback! :)