ES6 magical stuffs — Spread (…) syntax in depth

Maya Shavin
Frontend Weekly
Published in
8 min readFeb 5, 2018
Different ways of declaring parameters for SpreadButterOn method

A classic problem

Did you ever get into a situation where you need to create a method that receives an indefinite number of arguments? It’s not difficult, you would say, since Javascript provides us the almighty ‘arguments’ as local Array-like (pay attention — Array-like is not an array) object to use within each function. So let’s say if we want to create a method to calculate sum of numbers, not limited to how many numbers passed to it, we can implement as:

function sumOf(){
var sum = 0;
for (var i = 0; i < arguments.length; i++){
sum += arguments[i];
}

return sum;
}

Easy piece right? And of course, it works just fine.

sumOf(1,2,3,4,5);//15
sumOf(1,2);//3

Cool. But… what if we need a method that receives one parameter and unlimited number of arguments after that, because we can only decide based on the first argument what to do with all the other passed arguments (only with them, not including the first one)? For example:

function execute(action, <param1, param2, param3,...>){
if (action === <something>){
doSomething(<param1, param2, param3, ...>)
}
}

How do you achieve this? You may

Try apply()

Nice thing about apply() is that apply() takes single array of parameters (or array-like object) as its arguments, which means we can do:

The downside is it takes too much work:

  • Need to remove the first argument from the arguments list.
  • Since arguments is only array-like, there is no built-in shift() or splice(), we have to convert it to array first and only then remove the first argument.

The rest is simple — call the action using apply() and pass null as this value, and the modified array.

Question: is there any shorter, nicer way to achieve same result?

Thanks to ES6, we finally have the magic of (…) spread syntax.

What is exactly the magic spread syntax?

Simply, spread syntax — written as ‘…’ — allows:

  • Iterable object
  • Object expression

to be expanded in places where it is expected to have:

  • Zero
  • Indefinite number of arguments (function calls)
  • Indefinite number of elements (array literals)
  • Indefinite number of key-value pairs (object literals)

How does it solve our problem?

First and foremost, we can use spread syntax as Rest parameter.

Rest parameter

Rest parameter allows function to receive indefinite number of arguments as an array.

function <function name> (…args)

Note: spread syntax (…) needs to be used as a prefix of the last named parameter.

Unlike arguments, Rest parameter:

  • is actually instances of Array, with full support Array built-in functionalities.
  • only contains the ones that haven’t been given a separate name as array elements, while arguments contains all parameters passed.
function printMe(a,b,c){
console.log(arguments);
//will print all passed parameters respectively as a, b, c
}
function printMeSpread(a, ...args){
console.log(args);
//Will only print from 2nd passed parameters onwards
}
printMe(1,2,3);// [1,2,3]
printMeSpread(1,2,3); //[2,3]

Hence, instead of converting arguments to array, removing the first element (which we don’t need), we can just re-write our function declaration as:

function executeWithSpread(type, ...params){....}

And thanks to it, we saved about 2–3 lines of code. What can be done next?

Getting rid of apply()

Indeed, we can use it as replacement for apply() , then our function will become:

Here when we use ‘…’ syntax as prefix of params, all elements of params will be spread (expanded) as arguments, instead of being one argument params.

Further explanation:

  • On executeWithSpread(), params will converted to an array containing [1,2,3,4,5] by using ...params in method declaration.
  • On Sum(), by passing params as ...params, arguments will be [1,2,3,4,5] instead of [[1,2,3,4,5]]

And hence no need for null or any further unnecessary noise.

But if you still keen on combining with apply(), we can still do as following:

Either way, it’s still cleaner, lesser chance for bug, don’t you agree?

But not only that, as said, spread syntax does magic. And by “magic”, I mean it is a powerful tool, despite its simplicity in appearance. So…

What else does it offer to us?

Clone an object

One of the common way in copying an object is to use Object.assign() to create shallow clone version.

Spread syntax gives us a shorter and cleaner way

Similar to Object.assign() using spread (…) syntax will only initialize copying object’s own enumerable properties to new object.

Clone an array

Instead of using shallow copying an array using Array.slice(0)

Or using Array.from()

We can use Spread syntax to achieve the same effect —but remember, it will only go one depth level, same as Array.slice() and Array.from()

Convert an iterable object to array

Probably a lot of us already knew and used this functionality 😃 — remember the infamous Remove duplicates from given array problem? 😁

Remove duplicates of array

Yup, instead of using a loop to iterate and copy each element to new array, we can shorten the code by using Spread (…) syntax as above. It will spread all elements of Set into new array respectively without a lot of efforts required (of course, less chance for 🐛).

Same can go for converting a Map object to array, in which each of pair <key, value> will be represented as sub-array element in the new array.

But definitely not being able to convert Object, since it’s not iterable.

Merge arrays

Normally, if we want to merge two arrays or more into one, or simply just want to concatenate one array to another, we will use

  • Array.prototype.concat() which will return new array in which, all elements of additional array will be added to the end of original array. This won’t modify original array.
  • Array.prototype.unshift() which will add all elements of additional array to the beginning of original array. This will indeed modify original array.

However, with Spread syntax, we can define the merging order easily as:

Note that in this case, both original arrays are not modified.

Merge objects’ properties

Literally, which one do you prefer? This way (which modifies original object)

for(var key in obj2){
obj1[key] = obj2[key];
}

Or simply this way

Object.assign(obj1, obj2)

Or even simpler? (not modifies original object)

var mereObj = {...obj1, ...obj2}

Using with constructor

When using constructor withnew syntax, it is impossible to directly use apply() and array together — in ES5, in order to create new Array from existing array, we will have to do as following:

var arr = new (Function.prototype.bind.apply(Array, [null].concat([1,2,3,4])));

Is it not confusing enough? At least for me it’s. Luckily, Spread syntax is there for us:

var arr = new Array(...[1,2,3,4]);

Shouldn’t we appreciate this magic 😃?

Other mix-in usages

Last but not least, with the help of Spread syntax, we can finally have much more powerful array literal, where we don’t have to ask for the help of additional functionalities like push, splice, concat, etc… when needed.

var arr1 = ['hello', 'there'];
var arr2 = ['today', 'is', 'good', 'day', ...arr1, 'how', 'are', 'you']
console.log(arr2); //['today', 'is', 'good', 'day', 'hello', 'there', 'how', 'are', 'you']

Awesome right?

And of course, there is no limit to number of times we can use this syntax in array literal.

Conclusion

Personally I found this syntax is rather useful than following the traditional way — which requires long line of code and if you are not careful, you may end up with a 🐛without noticing, and that, for me, is really annoying.

However, in exchange for cleanness and convenience, sometimes you may find it harder to read/understand. (after all, for the first time-r(s), who will be able to understand what a bunch of ‘…’ stands for?)

Regardless, as one of #1 rules in programming stated — rule of KISS (Keep It Simple Stupid), we should always try to keep our code simple as much as we can, and less code but good code is never bad (especially in JS when number of code lines need to be loaded DOES matter to web performance).

So why not? If it is a good magic, we’d better take advantage of it, as a better upgrade for our own magic creation. 😄

Do you agree with me? Let me know in comments. I’d love to hear your opinions.

--

--