Exploring the Spread Operator and Rest Parameter in JavaScript
The spread operator has many use cases. Let’s go over a few of them
In this article, I am going to explain what the spread operator and rest parameter are in JavaScript. But first, let’s see what a logical operator is in JavaScript.
Logical Operators
&&
and ||
are known as logical operators. The logical operators in JavaScript do not return only boolean like in other programming languages. The logical operators in JavaScript return a value. That’s why many developers call them select operators.
Rules:
- truthy
&&
whatever = whatever - falsy
&&
whatever = falsy - truthy
||
whatever = truthy - falsy
||
whatever = whatever
But what are truthy and falsy? A value is truthy if you pass it in the Boolean
constructor as an argument and it returns true
. If it returns false
, it is falsy.
A simpler rule is that the values false
, null
, undefined
, 0
, -0
, ‘’
, NaN
, 0n
, and -0n
are falsy, while everything else is truthy.
const user = null;
const currentUser = user || {};
The example above is a simple way to add a default value. Because the user
is null
(falsy) the currentUser
will be {}
.
Spread Operator
The spread operator is an operator like +
, -
, /
, etc. that takes an iterable (something that we can loop through) and expands it to individual elements. The syntax is three dots (...
) and there are many use cases.
Shallow copy an object
Objects are non-primitive types that are saved by reference. If an object is equal to another object, when you change the first object, the second object is automatically changed.
const user1 = { name: 'George', surname: 'ruby' };
const user2 = user1;
user2.surname = 'Roubie';
In the example above, the surname in user1
is also changed. We can use the spread operator to fix it.
const user1 = { name: 'George', surname: 'ruby' };
const user2 = { ...user1 };
user2.surname = 'Roubie';
We can optimize the code above like this:
const user1 = { name: 'George', surname: 'ruby' };
const user2 = { ...user1, surname: 'Roubie' };
This code works because if you have the same key twice inside an object, the last one wins. This feature is very useful in state management techniques.
A shallow copy means that if there are nesting objects inside the copied object, the reference will remain.
const roles = { isAdmin: true };
const user1 = { name: 'George', surname: 'Roubie', role: roles };
const user2 = { ...user1 };
user2.role.isAdmin = false;
In the example above, the role is also changed in user1
, so be careful.
Combining objects
With the spread operator, we can combine as many objects as we want to a new one.
const userInfo = { name: 'George', surname: 'Roubie' };
const userRoles = { roles: ['admin', 'it'] };
const token = { accessToken: '123', refreshToken: '456' };
const currentUser = { ...userInfo, ...userRoles, ...token };
Conditionally add properties to an object
You can easily add properties to an object with the help of the logical operator &&
and the spread operator.
const getRole = () => 'admin';
const user = {
name: 'George',
surname: 'Roubie',
...(getRole() === 'admin' && { admin: true })
};
Because getRole() === 'admin'
returns true
, &&
returns { admin: true }
. That’s why admin: true
is added in the user object.
const getRole = () => 'dev';
const user = {
name: 'George',
surname: 'Roubie',
...(getRole() === 'admin' && { admin: true })
};
Now getRole() === 'admin'
returns false
and &&
returns false
. But if you spread the false
(e.g. { ...(false) }
), the spread operator will return nothing ({}
). That’s why nothing is added in the user object.
Copying an array (shallow)
Arrays are non-primitive types like objects. If an array is equal to another array, when you change the first array, the second array is automatically changed.
const a = [1, 2, 3];
const b = [...a];
b.push(4);
The code above can be also implemented with the slice method.
const a = [1, 2, 3];
const b = [...a, 4];
But with the slice method, we can’t add a value when we copying an array, as we did in the example above.
This copy is shallow, so if you have objects or arrays inside the array, the reference will remain.
const data = { test: 1 };
const arr1 = [1, 2, 3, data];
const arr2 = [...arr1];
arr2[3].test = 3;
In this example, arr1
and arr2
will have the value { test: 3 }
in the third index of the array because the copy is shallow.
Concatenating arrays
With the spread operator, we can concatenate as many arrays as we want to a new one.
const a = [1, 2, 3];
const b = [4, 5, 6];
const c = [7, 8, 9];
const d = [...a, ...b, ...c];
If we want to concatenate two arrays, we can also do it with the concat
method, but the concat
method can only concatenate two arrays. With the spread operator, we can concatenate as many arrays as we want and we can also add values between the arrays, which we can’t do with the concat
method.
const a = [1, 2, 3];
const b = [5, 6, 7];
const c = [9, 10, 11];
const d = [0, ...a, 4, ...b, 8, ...c, 12];
Using an array as an argument
Because the spread operator expands an array to individual elements, we can pass an array as an argument in a function. First, let’s see how we can do it without the spread operator.
const userInfo = ['George', 'Roubie'];
const getUser = function(name, surname) {
console.log(name, surname);
};
getUser(userInfo[0], userInfo[1]);
With the spread operator, we can easily do it like this:
const userInfo = ['George', 'Roubie'];
const getUser = function(name, surname) {
console.log(name, surname);
};
getUser(...userInfo);
This is very helpful when there are multiple arguments.
Rest Parameter
The syntax is the same as the spread operator’s, but it’s not an operator. In fact, it’s the exact opposite of the spread operator and combines the remaining elements into a single element.
const user = function (name, age, ...hobbies) {
console.log(name);
console.log(age);
console.log(hobbies);
}
user('George', 30, 'coding', 'killing zombies');
The hobbies
parameter is now an array with the value of ['coding', 'killing zombies']
.
var user = function (name, age, ...hobbies) {
console.log(name);
console.log(age);
console.log(hobbies);
}
user('George', 30);
When there are no values, the rest parameter makes hobbies
an empty array. This is very helpful because we don’t have to worry when we don’t add extra values.