Spread vs Rest Operators in JavaScript

baroblesvi

Barbara Robles (english prof.)

Posted on March 12, 2024

Spread vs Rest Operators in JavaScript

Content:

Let's learn how to use the three dots in our code. After this article, you won't forget the difference between them! šŸ±šŸ±šŸ±

Definition :

  • The spread is the operator that allows us to expand iterables into individual elements.

  • The rest is the operator we use to represent an indefinite number of arguments in an array.

Both are written using the three dots ..., but you'll see that it's easy to identify when it's a spread and when it's a rest! At the end, there's also a good tip for you!

Spread Operator

Concatenating Arrays

The spread allows us to combine two or more arrays, maintaining a concise and clean language.

Let's have some coffee? ā˜•ļø Consider the two arrays below:

const coffee = ['coffee', 'water'];
const spices = ['cinnamon', 'nutmeg', 'cardamom'];
Enter fullscreen mode Exit fullscreen mode

We can use the spread to mix the ingredients into a single array:

const coffeeReady = [...coffee, ...spices];

console.log(coffeeReady) 
// output:
// ['coffee', 'water', 'cinnamon', 'nutmeg', 'cardamom'];
Enter fullscreen mode Exit fullscreen mode

Simple! Much better than writing item by item from each of the two arrays to form coffeeReady.

Important:

1. Changes in coffee will NOT affect coffeeReady!

To better understand: when we make a copy, we can either create a new reference to the original value or just copy the value. Creating a new reference is simply creating a variable that will point to the same location in memory where the original value is.

If we had created a new reference, any changes in coffee would change coffeeReady and vice versa. But what we did with the spread was to copy only the value, which in turn will be stored in another location in memory. Thus, changes in one array will not affect the other.

However, some details could change this picture! That's because...

2. The spread only creates a shallow copy!

This means that, depending on the data contained in coffee, some changes could indeed alter coffeeReady! If coffee contained some non-primitive value, the computer would have created a reference to the values in memory. So, any change in one array would affect the other, since both would be storing a reference to the same location in memory. See below:

let a = [1, [2, 3]];
// [2, 3] is an array nested in a, and therefore
// it is a non-primitive value
const b = [4, 5, 6];

let c = [...a, ...b];
console.log(c);
// output: [1, [2, 3], 4, 5, 6]

a[0] = 11;
a[1][0] = 22;

console.log(c);
// output: [1, [22, 3], 4, 5, 6]
Enter fullscreen mode Exit fullscreen mode

See above that changing a[0] did not affect c, because we changed a primitive value. In other words, a[0] and c point to equal values, but they are in different locations in memory. However, changing a[1][0] modified c, because we changed the value to which both a[1][0] and c point.

Merging Objects

We can also merge objects into one using the spread:

const myParents = { 
  fathersName: 'Michael', 
  mothersName: 'Louise'
};
const mySiblings = { 
  brothersName: 'Philip', 
  sistersName: 'Lara' 
};

const myFamily = { ...myParents, ...mySiblings };
console.log(myFamily);
/* output:
{ 
  fathersName: 'Michael', 
  mothersName: 'Louise', 
  brothersName: 'Philip', 
  sistersName: 'Lara' 
} 
*/
Enter fullscreen mode Exit fullscreen mode

However, it is important to remember that the spread does not clone identical properties! Below we have two objects with brothersName:

const myParents = { 
  fathersName: 'Michael', 
  mothersName: 'Louise',
  brothersName: 'Gabriel'
};
const mySiblings = { 
  brothersName: 'Philip', 
  sistersName: 'Lara' 
};

const myFamily = { ...myParents, ...mySiblings };
console.log(myFamily);
/* output:
{ 
  fathersName: 'Michael', 
  mothersName: 'Louise', 
  brothersName: 'Philip', 
  sistersName: 'Lara' 
}
*/
Enter fullscreen mode Exit fullscreen mode

Note that the final object does not inherit both brothersName keys. In fact, only one prevails, which is from the second object.

Copying Arrays and Objects

Got the idea so far? If we can merge arrays and also objects, that means we can also copy them individually:

// creating a shallow copy of coffee:
const coffee = ['coffee', 'water'];
const coffeeCopy = [...coffee];
console.log(coffeeCopy)
// output: 
// ['coffee', 'water'];

// creating a shallow copy of mySiblings:
const mySiblings = { 
  brothersName: 'Philip', 
  sistersName: 'Lara' 
};

const myFamily = { 
  fathersName: 'Michael', 
  mothersName: 'Louise',
  ...mySiblings
};
// Now we can treat brothersName and sistersName as 
// a property of myFamily:
console.log(myFamily.brothersName) 
// output: Philip
Enter fullscreen mode Exit fullscreen mode

Turning Strings into Arrays

It is also possible to use the spread to turn a string into an array. This allows us to have more flexibility when manipulating strings, since we will be able to apply array methods to strings:

const str = 'coffee';
const letters = [...str, 's.', 'ā˜•ļø']; 
console.log(letters);// ["c", "o", "f", "f", "e", "e", "s.", "ā˜•ļø"]
Enter fullscreen mode Exit fullscreen mode

Rest Operator

As mentioned above, the rest operator is used to pack elements into an array. You'll see that the rest operator is a great ally when dealing with many values or an uncertain number of values.

Function Parameters

The rest operator allows us to represent an indefinite number of arguments as an array.

const order = function(beverage, ...otherIngredients) {
  console.log(beverage);
  console.log(otherIngredients);
};

order('green tea', 'milk', 'brown sugar'); 
// output:
// green tea 
// ['milk', 'brown sugar']
Enter fullscreen mode Exit fullscreen mode

Note that this allows us to call the same function with more arguments, as the rest operator will put them all in the otherIngredients array:

const order = function(beverage, ...otherIngredients) {
  console.log(beverage);
  console.log(otherIngredients);
};

order('green tea', 'milk', 'brown sugar', 'mint', 'tonka'); 
// output:
// green tea 
// ['milk', 'brown sugar', 'mint', 'tonka']
Enter fullscreen mode Exit fullscreen mode

šŸµ An important detail is that the rest must be the last parameter in the function! If we wrote function(...otherIngredients, beverage) the computer wouldn't know when to stop and this code would generate an error.

Destructuring with Rest and Spread

Rest and spread are also widely used in destructuring. If you don't know what that is yet, I suggest my other two articles here: array destructuring and object destructuring.

Using rest:

const [a, b, ...others] = [1, 2, 3, 4, 5];
console.log(a, b, others); 
// output: 1 2 [3, 4, 5]
Enter fullscreen mode Exit fullscreen mode

Now, take this tip so you don't confuse rest with spread here: the rest is on the left side of = and the spread is on the right side of =.

...rest = ā˜•ļø

šŸµ = ...spread

Thanks for making it this far! I hope this post has helped you! Thoughts? Please, comment below!

šŸ’– šŸ’Ŗ šŸ™… šŸš©
baroblesvi
Barbara Robles (english prof.)

Posted on March 12, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Spread vs Rest Operators in JavaScript
javascript Spread vs Rest Operators in JavaScript

March 12, 2024

Debounce using ES6
javascript Debounce using ES6

March 4, 2024

Object Destructuring in JavaScript
javascript Object Destructuring in JavaScript

August 21, 2023