REST vs SPREAD OPERATORS IN JAVASCRIPT

JavaScript uses three dots(...) for both the rest and spread operators. But the two operators are not the same.

The main difference between rest and spread operators is that the rest operator puts the rest of some specific user supplied values to a function into a JavaScript array. But, the spread operator expands iterables into individual elements.

For instance, consider the below code:

function deriveDetails(firstName, lastName, ...otherInfo) {
    return otherInfo;
}
deriveDetails("Ravi","Sharma","Coding","Web Developer","Male");
//Expected Output: ["Coding","Web Developer","Male"]

Now, consider the below example of spread operator:

function deriveDetails(firstName, lastName, companyDetails) {
    return `${firstName} ${lastName} works at ${companyDetails}`;
}
deriveDetails(...["Ravi","Sharma","XYZ"]);
// Expected Output: "Ravi Sharma works at XYZ"

REST OPERATORS:

The rest operator is used to put the rest of some specific user-supplied values into a JavaScript array.

Syntax: ...yourValues

The three dots in the snippet above symbolize the rest operator. The text after the rest operator references the values you wish to encase inside an array.

In JavaScript functions, rest gets used as a prefix of the function's last parameter.

Note: You cannot use "use strict" inside a function containing a rest parameter, default parameter or destructuring parameter

function printName(...value) {
    "use strict";
    return value;
}
//The definition above will return
"Uncaught SyntaxError: Illegal 'use strict' directive in function with non-simple parameter list:

But suppose you need your function to be in strict mode while also using the rest parameter. In such a case, you can write the "use strict" directive outside the function.

"use strict"
function printName(...value) {
    return value
}
printName("Ravi","Sharma");
// Prints ["Ravi","Sharma"]

How the REST operator works in destructuring assignment

The rest operator typically gets used as a prefix of the destructuring assignment's last value.

const [firstName, lastName, ...otherInfo] = [
"Ravi","Sharma","Coding","Web Developer","Works at XYZ"
];
console.log(otherInfo);
//["Coding","Web Developer","Works at XYZ"]

The rest operator (...) instructs the computer to add the rest of the user-supplied values into an array. Then, it assigns that array to the otherInfo variable.

//Define a destructuring object with two regular variables and one rest operator
const { firstName, lastName, ...otherInfo } = {
    firstName: "Ravi",
    lastName: "Sharma",
    companyName: "XYZ",
    profession: "Web Developer",
    gender: "Male"
}
console.log(otherInfo);
// Returns 
{companyName: "XYZ", profession: "Web Developer", gender: "Male"}

In other words, whenever, you use rest in a destructuring object, the rest operator will produce a properties object.

However, if you use rest in destructuring array or function, the operator will yield an array literal.

ARGUMENTS vs REST OPERATORS:

Difference-1: The "arguments" object is an array-like object - not a real array!

JavaScript arguments is not a real array. Instead, it is an array-like object that does not have comprehensive features of a regular JavaScript array.
The rest parameter, however, is a real array object. As such, you can use all array methods on it.

So, for instance, you can call the sort(), map(), forEach() or pop() method on a rest parameter. But, you cannot do the same on the arguments object.

Difference-2: You cannot use the "arguments" object in an arrow function.

The arguments object is not available within an arrow function, so you can't use it there. But you can use the rest parameter within all the functions including the arrow functions.

Difference-3: Let rest be your preference:

It is best to use rest parameters instead of the arguments object- especially while writing ES6 compatible code.

What is the Spread Operator and how does spread work?

The spread operator(...) helps you expand iterables into individual elements. The spread syntax works within array literals, function calls and initialized property objects to spread the values of iterable objects into seperate items. So, effectively, it does the opposite thing from the rest operator.

Note: A spread operator is effective only when used within array literals, function calls or initialized properties objects.

const myName = ["Ravi", "Sharma"];
const aboutMe = ["My name is", ...myName];
console.log(aboutMe);
// Returns
["My name is", "Ravi", "Sharma"]

Note:

  1. Alterations to myName will not reflect in aboutMe because all the values inside myName are primitives. Therefore, the spread operator simply copied and pasted myName's content into aboutMe without creating any reference back to the original array.

  2. The spread operator only does shallow copy. If myName container any non-primitive value, the computer would have created a reference between myName and aboutMe.

  3. Suppose, we did not use the spread syntax to duplicate myName's content. If we have written const aboutMe = ["My name is", myName]. In such case, a reference would have been assigned. Any change made in the original array would reflect in the duplicated one.

How to use Spread to convert a String into individual Array Items:
const myName = "Ravi Sharma"
console.log([...myName]);
//Returns
['R','a','v','i',' ','S','h','a','r','m','a']
How to use Spread Operator in function calls
const numbers = [1, 3, 5, 7];
function sumUp(a, b, c, d) {
    return a + b + c + d;
}
console.log(sumUp(...numbers));
// Returns
16

If the numbers array had more than 4 elements, only first 4 are considered and rest are ignored.

const myName = "Ravi Sharma"
function deriveName(a, b, c) {
    return a + b + c;
}
console.log(...myName); Returns Rav
console.log(...myName[3]); // Returns Rundefinedundefined
console.log([...myName]); // Returns "R,a,v,i, ,S,h,a,r,m,aundefined
console.log({...myName}); // Returns [object object] undefined undefined

How spread works in an Object literal:

const myNames = ["Ravi","Sharma"];
const bio = {...myNames, runs: "XYZ.com"};
console.log(bio);
// Returns
{0 : "Ravi", 1: "Sharma", runs: "XYZ.com"}

INFO-1: Spread Operators can't expand object literal's values

  1. Spread operator's can't expand object literal values: Since a properties object is not an iterable object, you cannot use the spread operator to expand its values. However, you can use the spread operator to clone properties from one object into another.

     const myName = {firstName: 'Ravi', lastName: 'Sharma'};
     const bio = {...myName, website: 'XYZ.com'};
     console.log(bio)l
     // Returns
     { firstName: 'Ravi', lastName: 'Sharma', website: 'XYZ.com'};
    
  2. The spread operator can expand iterable objects' values only.

  3. An object is iterable only if it(or any object in it prototype chain) has a property with a @@iterator key.

  4. Array, TypedArray, String, Map and Set are all built-in iterable types because they have the @@iterator property by default.

  5. A properties object is not an iterable data type because it does not have the @@iterator property by default.

  6. You can make a properties object iterable by adding @@iterator onto it.

INFO-2: The Spread Operator does not clone identical properties

Suppose, you used the spread operator to clone properties from Object A into Object B. And support Object B contains properties identical to those in Object A. In such case, B's versions will override those inside A.

const myName = {firstName: 'Ravi', lastName: 'Sharma'};
const bio = {...myName, firstName: 'Rahul', website:'XYZ.com'};
console.log(bio);
{firstName: 'Rahul', lastName:'Sharma', website:'XYZ.com'};

Observe that the spread operator did not copy myName's firstName property into the bio object because bio already contains firstName property.

Info 3: Beware of how spread works when used on objects containing non-primitives:

Suppose you used the Spread operator on an Object or array containing only primitive values. No reference will be created between the original object and duplicated object.

const myName = ["Ravi" , "Sharma", "is","my"];
const aboutMe = ["Rahul", ...myName, "name.."];
console.log(aboutMe);
["Rahult","Ravi","Sharma", "is","my", "name.."];

Every Item in myName is a primitive value. Therefore, when we used the spread operator to clone myName into aboutMe, hence the reference between arrays is not created. As such, any alteration you make to myName will not reflect in aboutMe

myName.push('real');
console.log(myName); 
["Ravi","Sharma","is","my"];
console.log(aboutMe);
["Ravi","Rahul","Sharma","is","my","name.."]; 
// "real" is not reflected in aboutMe

What if myName contains a non-primitive value?

In this case, spread will create a reference between the original non-primitive and the cloned one.

const myName = [["Ravi","Sharma"]];
const aboutMe = ["Rahul", ...myName, "name.."];
console.log(aboutMe);
["Rahul",["Ravi","Sharma"],"name.."]

Thanks for reading!!