6 Great Uses of the Spread Operator

By  on  

Thanks to ES6 and the likes of Babel, writing JavaScript has become incredibly dynamic, from new language syntax to custom parsing like JSX.  I've become a big fan of the spread operator, three dots that may change the way you complete tasks within JavaScript.  The following is a listing of my favorite uses of the spread operator within JavaScript!

Calling Functions without Apply

To this point we've called Function.prototype.apply, passing an array of arguments, to call a function with a given set of parameters held by an array:

function doStuff (x, y, z) { }
var args = [0, 1, 2];

// Call the function, passing args
doStuff.apply(null, args);

With the spread operator we can avoid apply all together and simply call the function with the spread operator before the array:

doStuff(...args);

The code is shorter, cleaner, and no need to use a useless null!

Combine Arrays

There have always been a variety of ways to combine arrays, but the spread operator gives use a new method for combining arrays:

arr1.push(...arr2) // Adds arr2 items to end of array
arr1.unshift(...arr2) //Adds arr2 items to beginning of array

If you'd like to combine two arrays and place elements at any point within the array, you can do as follows:

var arr1 = ['two', 'three'];
var arr2 = ['one', ...arr1, 'four', 'five'];

// ["one", "two", "three", "four", "five"]

Shorter syntax than other methods while adding positional control!

Copying Arrays

Getting a copy of an array is a frequent tasks, something  we've used Array.prototype.slice to do in the past, but we can now use the spread operator to get a copy of an array:

var arr = [1,2,3];
var arr2 = [...arr]; // like arr.slice()
arr2.push(4)

Remember: objects within the array are still by reference, so not everything gets "copied", per se.

Convert arguments or NodeList to Array

Much like copying arrays, we've used Array.Prototype.slice to convert NodeList and arguments objects and to true arrays, but now we can use the spread operator to complete that task:

[...document.querySelectorAll('div')]

You can even get the arguments as an array from within the signature:

var myFn = function(...args) {
// ...
}

Don't forget you can also do this with Array.from!

Using Math Functions

Of course the spread operator "spreads" an array into different arguments, so any function where spread is used as the argument can be used by functions that can accept any number of arguments.

let numbers = [9, 4, 7, 1];
Math.min(...numbers); // 1

The Math object's set of functions are a perfect example of the spread operator as the only argument to a function.

Destructuring Fun

Destructing is a fun practice that I'm using a ton of on my React projects, as well as other Node.js apps.  You can use destructuring and the rest operator together to extract the information into variables as you'd like them:

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
console.log(x); // 1
console.log(y); // 2
console.log(z); // { a: 3, b: 4 }

The remaining properties are assigned to the variable after the spread operator!

ES6 has not only made JavaScript more efficient but also more fun.  Modern browser all support the new ES6 syntax so if you haven't taken the time to play around, you definitely should.  If you prefer to experiment regardless of environment, be sure to check out my post Getting Started with ES6.  In any case, the spread operator is a useful feature in JavaScript that you should be aware of!

Recent Features

Incredible Demos

Discussion

  1. Two more:

    Convert iterables to Arrays (not just arguments and NodeList):

    > [...new Map().set(true, 'yes').set(false, 'no')]
    [ [ true, 'yes' ], [ false, 'no' ] ]
    

    Eliminate duplicates from an Array:

    > const arr = [7, 3, 1, 3, 3, 7];
    > [...new Set(arr)]
    [ 7, 3, 1 ]
    
    • James Meyers

      I love that unique function. You can even use a iteratee function to determine uniqueness (like Lodash’s uniqBy):

      [...new Map(array.map((value) => [iteratee(value), value])).values()]
      
  2. Rob Simpson

    @Axel – great share with the elimination of duplicate arrays. Very helpful.

  3. anudeepdasari

    Spread operator can also be used to convert a string to character array.

     
    const str = "hello";
    const strChar = [...str];
    console.log(strChar); // would display ['h', 'e',' l',' l', 'o']
    
  4. Jakob E

    Thanks for sharing :-)

    Regarding browser support (ES5) – TypeScript nor Babel seems to handle the NodeList to array
    and Array.from does not work in IE (ref: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from)

  5. Note that the ... operator used with arrays was added in ES6, but it is only a stage-3 addition to be able to use them with objects as shown. It’s likely to land, and Babel/etc have supported it for awhile, but it’s not quite standard. Should probably have at least a footnote in that section.

  6. | You can even get the arguments as an array from within the signature:

    AKA rest parameters.

  7. Florian

    Extending your first example you can use it as a (very hacky looking) way for conditional function arguments:

    doStuff(...condition ? [value] : [])
    
    // equals:
    
    if (condition) doStuff(value)
    else doStuff()
    

    This is really ugly but can be helpful at times

    • Emmveqz

      Pardon me, but what is the difference between your approach,
      and:

      doStuff(condition ? value : undefined)

      Which I find cleaner then both of your mentions, but again, I don’t know if this one is in some sort different.

    • Hm, I have to admit that my example did not clearly highlight what I wanted to achieve with it.

      What I wanted to show is that with the spread operator, you can conditionally spread or not spread values. This doesn’t get clear in an example with just one spreaded item though.

      Maybe it’s clearer to see the advantages when you have multiple items:

      doStuff(...condition ? [value1, value2, value3] : [])
      

      That said, just for the record: Your approach is almost equivalent to my original comment.
      The only difference would be if somebody checked for the number of arguments passed:

      // doStuff is a function which returns the number of arguments passed to it:
      const doStuff = (...args) => args.length
      
      // My approach returns 1 if condition is truthy, 0 otherwise.
      doStuff(...condition ? [value] : [])
      
      // Your approach always returns 1 because even though the argument may be
      // undefined, you still have passed it as an argument.
      doStuff(condition ? value : undefined)
      
  8. Remember, just because you can and it’s handy doesn’t mean you should. Depending on your use and if perf is something you have to worry about, test things first. The slice vs spread case, slice is still much faster. In a simple test with an array that has 100 ints, slice reported 4,420,016 ops/sec while using spread operator reported 219,263 ops/sec. That’s quite a large gap

  9. Use it like an optional/null check for nested properties

    if ({...{...top}.prop_one}.prop_two) { doThings() }
    
    • Paulo Griiettner

      That I did not know, Thanks

  10. Sebastian Lasse

    super helpful.
    btw @Axel not linking to http://2ality.com/2015/01/es6-set-operations.html is understatement ;)

  11. Just a note on naming, it sounds like the ec6 spec calls it the “spread element”. Is it an operator?

    https://stackoverflow.com/questions/36989741/how-can-i-concatenate-two-arrays-in-javascript#comment61533834_36989849

  12. One of my favorites is conditionally including properties

    {
    a:1,
    b:2,
    ...(check?{c:3}:{d:4})
    }
    
  13. Creating objects with values from a string can be very useful as well:

    const string = 'abcde';
    const obj = { ...string };
    // {0: "a", 1: "b", 2: "c", 3: "d", 4: "e"}
    
  14. You’re definitely right not to let us forget about Array.from. People can be so keen on using the spreads they forget this is more useful than ever. It should always be preferred when you’re trying to get a new array mapped from an iterator. [...myNodeList].map(mapFn) will make an array, then map it. Array.from(myNodeList, mapFn) will iterate directly into the mapper.

    I was surprised how clean this approach reads when used consistently too. Eg Array.from(document.querySelectorAll('p'), ({ textContent }) => textContent) is very semantic.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!