The Swiss Army Knife of ES6 | Part I: Spread Operator (…)

JavaScript ES6 has brought us a plethora of functionality improvements and sweet, sweet syntactic sugar. Among them, one of the most elegant and perhaps my favorite has to be these three little dots … known as the “spread operator” and “rest parameter” (depending on syntactic context, but more on that later).
In summary, the ES6 spread operator does… Well, exactly what it promises — spreads an iterable (array, string or object*) into individual elements. The rest parameter, on the other hand, allows us to represent an indefinite number of arguments as an array.
*official support for use with objects was added in ES9 (late 2018)
However, simple expansion and representation of data-sets and parameters are merely the two basic blades of the functionality multi-tool these three little magical dots provide us (or else this would be a pretty pointless write-up, right?).
In this two part blog, we will take a look at some incredibly useful and practical ways we can make use of the spread operator and rest parameter, as well as the prior ES5 methods they have been intended to improve upon.

Before we take our dive into the actual cool and shiny features that the spread operator and rest parameter offers, let’s first briefly clarify a common misconception that surrounds the two, which is:
“spread operators and rest parameters are the same thing.”
This is incorrect.
Despite being visually represented by the same exact three dots (…), the two in fact performs “opposite” tasks. To reiterate, while the spread operator expands an array/iterable into individual elements, the rest parameter condenses individual elements back into an array.
Though it seems easy to confuse them, it is actually fairly easy to distinguish the two. All you have to remember to tell them apart is that the rest parameter can ONLY be used as the last element in a function’s parameters.
In any context where you find the ellipsis outside of a function’s final parameter, it is a spread operator. It’s as simple as that.

SPREAD STRINGS
Let’s begin with a simple example of spreading a string. While this might not be a super practical functionality, it provides a good visual demonstration of the basic “spreading” that the spread operator performs.
Spreading the string with Array.prototype.split('')
const meow = "kitty-cat";
const spreadCat = meow.split('')console.log(spreadCat)
// logs [ 'k', 'i', 't', 't', 'y', '-', 'c', 'a', 't' ]
Spreading the string with the spread operator
const meow = "kitty-cat";
const spreadCat = [...meow]console.log(spreadCat)
// logs [ 'k', 'i', 't', 't', 'y', '-', 'c', 'a', 't' ]

COPY ARRAYS / OBJECTS
One of the most frequently repeated tasks in JavaScript is making copies of array and object literals. The spread operator shortens the syntax for us noticeably.
Making an array copy with Array.prototype.slice()
const kittens = ['persian','bengal','siamese'];
const newLitter = kittens.slice();console.log(newLitter)
// logs [ 'persian', 'bengal', 'siamese' ]
Making an array copy with spread operator
const kittens = ['persian','bengal','siamese'];
const newLitter = [...kittens];console.log(newLitter)
// logs [ 'persian', 'bengal', 'siamese' ]
Making an object copy with Object.assign()
const cat = {
name: "Mittens",
legNum: 4,
hasTail: true
};const catClone = Object.assign({}, cat);console.log(catClone)
// logs { name: 'Mittens', legNum: 4, hasTail: true }
Making an object copy with spread operator
const cat = {
name: "Mittens",
legNum: 4,
hasTail: true
};const catClone = {...cat};console.log(catClone)
// { name: 'Mittens', legNum: 4, hasTail: true }
CONCATENATE ARRAYS
Not that concatenation has ever really been difficult, but the spread operator allows us to do it in a concise way, even when when concatenating multiple arrays.
Concatenation with Array.prototype.concat()
const cats1 = ['Mittens', 'Stewart', 'Tom'];
const cats2 = ['Snowball', 'Felix', 'Whiskers'];const catFriends = cats1.concat(cats2);console.log(catFriends)
// [ 'Mittens', 'Stewart', 'Tom', 'Snowball', 'Felix', 'Whiskers' ]
Concatenation with spread operator
const cats1 = ['Mittens', 'Stewart', 'Tom'];
const cats2 = ['Snowball', 'Felix', 'Whiskers'];const catFriends = [...cats1, ...cats2];console.log(catFriends)
// [ 'Mittens', 'Stewart', 'Tom', 'Snowball', 'Felix', 'Whiskers' ]
COMBINE ARRAYS / OBJECTS
We can use .push(), .unshift(), and .splice() to easily combine elements with arrays, but combining multiple arrays freely pre-ES6 has never been a straightforward process. While .concat() gets the job done, it provides little freedom. The spread operator not only lets us combine arrays in any order we would like, but do so without any hassle.
Combining arrays with spread operator
const cats1 = ['Mittens', 'Stewart'];
const cats2 = ['Snowball', 'Felix'];
const cats3 = ['Hobbs', 'Scratchy']const catParty = [...cats1, 'Tom', 'Whiskers', ...cats2, 'Mimi', ...cats3]console.log(catParty)// logs:
[ 'Mittens', 'Stewart', 'Tom', 'Whiskers', 'Snowball', 'Felix', 'Mimi', 'Hobbs', 'Scratchy' ]
Objects, being un-ordered, were easier to combine than arrays with help of Object.assign(), but the spread operator still makes the task easier.
Combining objects with Object.assign() / spread operator
const catProps = {
legNum: 4,
tailNum: 1,
hasClaws: true,
}const catProps2 = {
favFood: "fish",
sound: "meow",
likesWater: false
}const cat = Object.assign(catProps, catProps2)
const catSpread = {...catProps, ...catProps2}console.log(cat)
console.log(catSpread)// logs (same result for both methods):
{ legNum: 4,
tailNum: 1,
hasClaws: true,
favFood: 'fish',
sound: 'meow',
likesWater: false }

CALL FUNCTIONS IN PLACE OF “.apply()”
When we want to pass in an array/list of arguments to a function, the common convention has been to use Function.prototype.apply(). It works, but once again, feels un-intuitive and leaves room to be misused by those unfamiliar with the method. ES6 allows us to use the spread operator as a simple replacement (in most cases) where we would normally use .apply().
Calling function with Function.prototype.apply()
function printCats (a, b, c) {
console.log(a + ", " + b + ", and " + c)
};const cats = ["Felix", "Tom", "Mimi"];printCats.apply(null, cats);// logs "Felix, Tom, and Mimi"
Calling function with the spread operator
const printCats = (a, b, c) => {
console.log(`${a}, ${b}, and ${c}`)
};const cats = ["Felix", "Tom", "Mimi"];printCats(...cats);// logs "Felix, Tom, and Mimi"
But wait! What about “this”?
By default, using the spread operator alone to call a function with an array is equivalent to passing in “undefined” as the first parameter of .apply() (i.e. <object>.apply(undefined, <array>)). This can be an issue where we need to provide context to this.
However, all we have to do to remedy the situation is place the object we want to bind this on the left of a dot at call-time… sound familiar? 😏
const catObj = {
favFood: "fish",
sound: "meow",
fur: "soft",
describeCat: function(a, b, c){
//note: cannot use ES6 arrow function due to lexical scoping
console.log(`I like to ${a} ${this.favFood}.`);
console.log(`The sound I ${b} is ${this.sound}.`);
console.log(`${c} my ${this.fur} fur.`);
}
};const actions = ["eat", "make", "Stroke"];catObj.describeCat(...actions);
//this is equivalent to: catObj.describeCat.apply(catObj, actions);//logs:
I like to eat fish.
The sound I make is meow.
Stroke my soft fur.
NOTE: Though the spread operator allows us to remove complexity and replace .apply() in most circumstances, we cannot use it to set this in any way other than in context of calling a function. In other words, if the value of this needs to be set explicitly, using .apply() is still the way to go.
BONUS: MATH
Aside from its explicitly designed uses we just covered above, the spread operator can also be useful in many other creative ways. One of these situations is passing in an array of numbers to Math.prototype methods.
Here is a quick example using Math.max()
const catsAge = [2, 4, 7, 1];
const oldestAge = Math.max(...catsAge);console.log(oldestAge);
//logs 7//if we were to do this without the spread operator, we would need to implement an extra step to convert the catsAge array to individual numbers (i.e. with Array.values(catsAge) or similar)

Congrats on making it to the end! Now go and make use of the spread operator in your code! Check back next week for part II of our exploration into the rest parameter, and the wonderful world of restructuring! 😺