Both of them used the (...
) syntax (three-dot). even though they are syntactically are the same, they differ from what they do. here are my unofficial (or how i tell my friend) definition of what is rest parameter and spread operator, starting with the rest parameter.
Rest parameter
How I tell my friend:
Rest Parameter: it’s always going to be used at the end of array/object(when you are using destructuring on array or object) or function parameters. and you are going to used to collect/gather the remaining element into an array (it can be an object when you are destructuring an object)
Official:
The rest parameter syntax allows a function to accept an indefinite number of arguments as an array.link
Rest Operator example:
function sum(first, ...rest) {
for (var i = 0; i < rest.length; i++) {
first += rest[i];
}
return first;
}
console.log(sum(1, 2, 3, 4)); // answer: 10
here as you can see the first
is equal to 1 and the ...rest
is equal to [2, 3 ,4]
. so basically the ...rest
is collecting/gathering the remaining arguments which are [2, 3, 4]
.
And it doesn’t matter you can pass as many arguments as you like here is the example:
function sum(first, ...rest) {
for (var i = 0; i < rest.length; i++) {
first += rest[i];
}
return first;
}
console.log(sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)); // answer: 91
Now if you are use the ...rest
at first you are going to see an error:
function sum(...rest, first) {
for (var i = 0; i < rest.length; i++) {
first += rest[i];
}
return first;
}
console.log(sum(1, 2, 3, 4));
//function sum(...rest, first) {
^
//SyntaxError: Rest parameter must be last formal parameter
Next examples are going to be about when you are using Destructuring assignment. (learn about Destructuring assignment)
const test = ["daniel hemmati", "yourname", "age", 1, 2, 3];
const [zeroIndex, ...rest] = test;
console.log(zeroIndex); // 'daniel hemmati'
console.log(rest); //[ 'yourname', 'age', 1, 2, 3 ]
Object destructuring
const goodOldObject = {
learning: "rest operator",
why: "because you are good developer",
happyCoding: true,
};
const { learning, ...rest } = goodOldObject;
console.log(learning); // rest opeartor
console.log(rest); // { why: 'because you are good developer', happyCoding: true }
Why not using arguments
object instead of rest parameter?
- first of all
arguments
is not a real array, it’s an array-like object so you don’t have access to map, filter, reduce and many other array methods on arguments. - And you can’t use arguments in arrow function.
And if even if you want to turn the arguments to the actual array, you have to add some boilerplate code.
Here is the comparison of the code length between using arguments vs rest parameter for sorting an array of numbers.
// with arguments object
function withArguments(a, b, c) {
// this will turn it to array
let normalArray = Array.prototype.slice.call(arguments);
// or use this method
let normalArray2 = [].slice.call(arguments);
// or this
let normalArray3 = Array.from(arguments);
return normalArray.sort((a, b) => a - b);
}
console.log(withArguments(3, 2, 1));
As you can see there are three ways to turn arguments
object into an array.
Sorting an array with rest operator.
// with rest operator
function withRest(...rest) {
return rest.sort((a, b) => a - b);
}
console.log(withRest(3, 4, 2, 1));
And as you can see we can pass as many arguments we want to withRest
function. (i mean come on, that’s amazing that we can do that with three dot).
Spread operator
Official:
Spread operator: (...
) allows an iterable such as an array expression or string to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected. source
How i tell my friend:
Spread operator: you have iterables like array / object / strings and you want to expand it into a single arguments/elements.
For now we just saw that how we can get an array form the list of parameters. like witRest
function.
But sometimes we want to do a exactly the reverse of that.
For instance we have a built-in function in Javascript Math.max
which return the greatest number from a list of numeric number:
console.log(Math.max(3,5,1))// answer: 5
Now what if we have array of number? Does it work if we pass an array of numbers?
let res = [2, 5, 1, 4, 9, 3];
console.log(Math.max(res)); // NaN
The reason is that Math.max
accept the numeric arguments not a single array. Now in the past in order to pass an array of numbers to a Math.max
you had to write some extra code to get the result you wanted. Here is the code:
let numbers = [3, 1, 0, 6, 4, 9, 5];
console.log(Math.max.apply(null, numbers)); // 9
Here is spread operator for rescue. instead of writing all of that Math.max.apply(null, number)
we can do this:
let res = [2, 5, 1, 4, 9, 3];
console.log(Math.max(...res)); // 9
Now that’s really cool. basically when ...res
is used on a function call, it expands an iterable object ...res
into the list of arguments.
NOTE: if you log the ...res
you will see the output is a list of number.
let res = [2, 5, 1, 4, 9, 3];
console.log(...res) // 2 5 1 4 9 3
You even pass multiple array of numbers and spread it to the Math.max
let input1 = [1, 5, 3, 0, 4, 7];
let input2 = [2, 5, 1, 4, 9, 3];
console.log(Math.max(...input1, ...input2));// answer: 9
Or even we can combine this two array with normal values like this:
let input1 = [1, 5, 3, 0, 4, 7];
let input2 = [2, 5, 1, 4, 9, 3];
console.log(Math.max(...input1, 10, 11 ...input2));// answer: 11
NOTE: unlike rest parameter it doesn’t matter where do you put spread operator, it can be at first, middle, or even at the end.
A better way to concatenate array using spread operator
In the past of we concatenate array like this:
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
arr1 = arr1.concat(arr2);
console.log(arr1);
Here is the spread syntax of it
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
arr1 = [...arr1, ...arr2];
console.log(arr1); // [ 1, 2, 3, 4, 5, 6 ]
Pass elements of an array to a function using spread operator
This is how we used to do it:
function spreadIt(a, b, c) {
return a + b + c;
}
let arr = [1, 2, 3];
console.log(spreadIt.apply(null, arr)); // answer: 6
Now we can do it with spread operator like this.
function spreadIt(a, b, c) {
return a + b + c;
}
let arr = [1, 2, 3];
console.log(spreadIt(...arr))
Copy array with spread operator
How we did it before
let arr = [1, 2, 3];
// this two basically copy array
// you are going to see more slice method rather Array.from()
let arr2 = arr.slice();
// older method
let arr3 = Array.from(arr);
console.log(arr2);// [1, 2, 3]
So if i push number to arr2
the arr
will not changed.
And we can do same thing with spread operator
let arr = [1, 2, 3];
let arr2 = [...arr];
console.log(arr2);// [1, 2, 3]
Spread operator in object literals
It’s basically like what we did in array but this time for object.
We can spread one object into another object OR we can merge two object into a new object OR we can copy the object
let daniel = { name: "daniel", lastname: "hemmati" };
let obj1 = { foo: "bar", x: 42 };
let obj2 = { foo: "baz", ...daniel, y: 13 }; // danile is spreaded into obj2 LOL
let mergeObject = { ...obj1, ...obj2 };
let copyObject = { ...mergeObject };
console.log(obj2); // { foo: 'baz', name: 'daniel', lastname: 'hemmati', y: 13 }
console.log(mergeObject); // { foo: 'baz', x: 42, name: 'daniel', lastname: 'hemmati', y: 13 }
console.log(copyObject); // { foo: 'baz', x: 42, name: 'daniel', lastname: 'hemmati', y: 13 }
Summary
That’s a warp for spread operator and rest parameter. so whenever you see ...
it’s either rest or spread operator.
The easy way to distinguish between them:
- When the
...
is at the end of the function parameter, it’s rest parameter, if it’s on the left side of assignment (when we did destructuring assignment) again it’s rest parameter. - The spread operator occurs on a function call, or on the right side of the assignment (when you spread one array into another array you did on the right side of the assignment)
left side of assignment = right side assignment