DEV Community

Cover image for 5 Uses for the Spread Operator
Laurie
Laurie

Posted on • Updated on • Originally published at tenmilesquare.com

5 Uses for the Spread Operator

The spread operator is a favorite of JavaScript developers. It's a powerful piece of syntax that has numerous applications.

So many, in fact, that it's often hard to keep track of them all. In this post we're going to review 5 of the most common uses for the spread operator.

Copying an array

This is one of the most common uses of the spread operator. Taking the contents of an array and "spreading it out" to fill another array.

let arr = [1,2,3,4]
let copy = [...arr]
// copy is [ 1, 2, 3, 4 ]
Enter fullscreen mode Exit fullscreen mode

Looked at in a different way, the spread operator is selecting each individual element inside the arr array and placing each of those elements in a new array structure.

Note that this is different than putting an array inside another array.

let arr = [1,2,3,4]
let copy = [arr]
// copy is [ [1, 2, 3, 4] ]
Enter fullscreen mode Exit fullscreen mode

That option gives you a multidimensional array.

Concatenate arrays

Building on the previous example, it turns out that you're able to take multiple arrays and spread them out in a new array. One after another.

let arr1 = [1,2,3,4]
let arr2 = [5,6,7,8]
let concat = [...arr1, ...arr2]
// concat is [ 1, 2, 3, 4, 5, 6, 7, 8 ]
Enter fullscreen mode Exit fullscreen mode

If we break it down as we did in the previous example, the spread operator is extracting each element in the initial arrays and placing it in the new array.

Pass arguments as arrays

This is where the spread operator starts showing off its versatility. In this example, we're passing three arguments into a function. The spread operator is used in front of an array with three elements inside of it.

function dev(x, y, z) { }

var args = [0, 1, 2]

dev(...args) // call function
Enter fullscreen mode Exit fullscreen mode

A good way to make sense of this is to look at our previous examples. What would happen if we used the spread operator on an array and never placed it inside a new array?

Each element in the array would stand on its own. This is that intermediate transformation. Each element stands on its own and hasn't been placed in a new data structure. Therefore, all three elements can be passed in as arguments to the function individually.

Copy an object

Not only can the spread operator be used for arrays, it can be used for objects as well. Just like copying the array before, we can copy an object.

let obj = {a: 1, b: 2, c: 3}
let copy = {...obj}
// copy is {a: 1, b: 2, c: 3}
Enter fullscreen mode Exit fullscreen mode

In this example, the spread operator extracts each key-value pair from obj and places them in a new object, copy.

And just like the array example, it's worth noting that this is different than putting an object inside another object.

let obj = {a: 1, b: 2, c: 3}
let copy = {obj}
// copy is { {a: 1, b: 2, c: 3} }
Enter fullscreen mode Exit fullscreen mode

Merge object

We can also merge two objects together using the spread operator.

let obj1 = {a: 1, b: 2, c: 3}
let obj2 = {d: 4, e: 5, f: 6}

let merge = {...obj1, ...obj2}
// merge is {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6}
Enter fullscreen mode Exit fullscreen mode

Again, we're extracting all the key-value pairs from the initial objects and placing them in a new object data structure.

Bonus - An Error!

Despite the fact that the spread operator works on both arrays and objects, you can't mix and match these data types.

let obj = {a:1, b:2, c:3}
let copy = [...obj] // this won't work!
Enter fullscreen mode Exit fullscreen mode

This makes sense if we think about it because when working on an array the spread operator is handling elements, and for an object, it's key-value pairs.

And there you have it!

That's a whole lot of uses for the spread operator, and those aren't even all of them. If you're looking for a full set, take a look at the mozilla docs.

And if you're interested in other JavaScript syntax that can help you write clean code, check out these articles!

Top comments (62)

Collapse
 
tbutterwith profile image
Tom Butterwith • Edited

Great examples!
Note that for merging objects, the keys in the later objects take precedence. e.g.

let obj1 = { a:1, b:2, c:3 };
let obj2 = { a:2 };

let obj3 = {...obj1, ...obj2};
// obj3 is { a:2, b:2, c:3 }
Enter fullscreen mode Exit fullscreen mode
Collapse
 
drewtownchi profile image
Drew Town

This is usually how I do optional options in an object.

If I have a function that accepts an object of options I'll spread them into the default options. It works great because all of the latter keys take precedence like you stated.

Collapse
 
jwkicklighter profile image
Jordan Kicklighter

This totally works, but have you tried using default values in the function signature? I believe that was added in ES6, but I can't remember for sure.

function (name: 'John', id: 5) {
  ...
}
Thread Thread
 
drewtownchi profile image
Drew Town

I have and it works well for simple function signatures. But, when you reach a point where you have 5, 6, 7 optional parameters and you need to change the last one this pattern becomes a real pain. The problem is there is no way to invoke named function parameters, you have to do it by order.

Thread Thread
 
jwkicklighter profile image
Jordan Kicklighter

If you add curlies, you can still specify the arguments in the signature but treat it as an object. Best of both worlds.

Collapse
 
felipperegazio profile image
Felippe Regazio

i do the same ;P

Collapse
 
laurieontech profile image
Laurie

Absolutely important to point out! Thanks for mentioning it.

Collapse
 
douglasbarbosadelima profile image
Douglas Barbosa de Lima

I like to use the Set Object with Spread Operator to display non-repeat data into Array:

const arr = [1, 2, 3, 1, 2, 3];
const newArr = [...new Set(arr)]; // [1, 2, 3]

Spread is a awesome feature.
Congratulations for the post!

Collapse
 
proticm profile image
Milos Protic

Note that this only works if you have an array of primitive types

Collapse
 
willvincent profile image
Will Vincent

Was going to leave the same comment :)

Collapse
 
laurieontech profile image
Laurie

Nice one!

Collapse
 
deleteman123 profile image
Fernando Doglio

Nice quick round up! Thanks! A quick word of warning, when copying an array, it does a shallow copy, so doing:

let myarr = [[1],[2]]
let copyArr = [..myarr]
copyArr[0][0] = 12

This will change myarr as well.

Collapse
 
laurieontech profile image
Laurie

Definitely. My understanding is that this only applies because the example above is multidimensional. So the first level is copied, but the deeper levels are referenced. If it's one-dimensional it is a deep copy.

Collapse
 
silvestricodes profile image
Jonathan Silvestri

Hey Laurie!

Good post, but just letting you know that your error example does work and will not throw an error.

  let arr = [1,2,3,4]
  let copy = {...arr}

Arrays are objects, where the index is the key. What you end up with in this example is:

  console.log(copy) // { 0: 1, 1: 2, 2: 3, 3: 4 }
Collapse
 
laurieontech profile image
Laurie

Oops! I think I meant to flip that around. Will fix, thanks for catching it.

Collapse
 
silvestricodes profile image
Jonathan Silvestri

No problem!

Collapse
 
docx profile image
Lukáš Doležal • Edited

Great stuff. Note that spread operator works also in deconstruction too, not only construction:

Take first (or n first) fields of an array:

let [first, ...rest] = [1, 2, 3, 4, 5];
// first == 1
// rest == [2, 3, 4, 5]

Or as function definition:

function giveMeAllTheParams(...params) {
  console.log(`You gave me ${params.length} params`);
}

giveMeAllTheParams(1, 2, 3, 4, 5);
// You gave me 5 params
Collapse
 
araw830 profile image
Atul Rawat

Good one😍

Collapse
 
gregbacchus profile image
Greg Bacchus • Edited

Likewise for objects, can be used to create a new one that omits certain properties.

const {a, ...rest} = {a: 1, b: 2, c: 3};
Collapse
 
laurieontech profile image
Laurie

All great examples!

Collapse
 
jwkicklighter profile image
Jordan Kicklighter

I always forget this since there seem to be more places to use the construction functionality. Thanks for the reminder!

Collapse
 
miguelrodoma95 profile image
Miguel Rodriguez

Hi Lauri, thank you for this post, I'm just learning JS and this really helped me to understand the spread operator.

As I'm a beginner in JS I have a question: in the "Pass arguments as arrays" part, I understand that x=0, y=1, and z=2. What would happen if my function is expecting 3 parameters but for some reason, the array has 2 or 4 elements? Or we would simply try to make sure that the size of the array and the parameters expected by the function are equal?

Collapse
 
laurieontech profile image
Laurie

The name of a function and the number of parameters it takes (the placeholders for arguments) represent something called a function signature. This is unique within the current scope of the program. If you were to pass too many arguments it would throw an error because it would not find a corresponding function signature, i.e. a function with that name taking 2 (or 4) elements.

Collapse
 
ajmath62 profile image
Aaron Klein

That may be the case in other languages, but not in JS. In JS, if you pass too many arguments, the extras will be ignored (but accessible through the variable named arguments). If you pass too few arguments, the missing arguments will be given the value undefined (which may result in an error, but not definitely).

Thread Thread
 
laurieontech profile image
Laurie

You're correct, I put my Java hat back on and shouldn't have!
Looks like this has some good explanations. stackoverflow.com/questions/126940...

Collapse
 
miguelrodoma95 profile image
Miguel Rodriguez

Got it, thank you!

Collapse
 
j3m5 profile image
J3m5

You can also use it to conditionally add properties to objects.

const obj = {
  a:1,
  ...true && {b:2},
  ...false && {c:3}
}

// {a:1,b:2}
Collapse
 
darksmile92 profile image
Robin Kretzschmar

I am using it in the React environment quite often and I'm curious if you have a clue about how the performance is compared to the equal methods like copying an array with spreading vs copying it via slice()?

Collapse
 
laurieontech profile image
Laurie

It depends on the JS engine running, i.e. chrome vs safari. My understanding is that slice used to be more performant but that gap has been cut significantly and on many browsers, the spread operator is the same, or possibly better performance wise.

And since slice() only works for arrays, the spread operator is a more powerful piece of syntax.

Collapse
 
darksmile92 profile image
Robin Kretzschmar

Thanks for the answer, wasn't aware of it but a quick research confirmed it :)

Collapse
 
jsalinas11 profile image
Jordan

Also, here's a nice quick way to go from Map to array of entries.

let map = new Map([['a', 1], ['b', 2], [{foo: 'bar'}, 3]]);
let entries = [...map]; // [['a', 1], ['b', 2], [{foo: 'bar'}, 3]]

// maybe useful to quickly stringify a map?
JSON.stringify([...map])
Collapse
 
kenbellows profile image
Ken Bellows

Great summary! Two extra thoughts:

  • Another handy use is to convert iterable objects to Arrays when you need the Array methods:
  /* Filtering a Set */
  const fruit = new Set(['apple','banana','avocado','watermelon'])
  // No no no!
  fruit.filter(f => f.startsWith('a')) // TypeError: fruit.filter is not a function
  // Instead...
  [...fruit].filter(f => f.startsWith('a')) // ["apple", "avocado"]

  /* Mapping over a generator's output */
  // counts from low to high-1
  function* range(low, high) {
    for (let i=low; i<high; i++) {
      yield i
    }
  }
  // No no no!
  range(1,10).map(n => n**2) // TypeError: range(...).map is not a function
  // Instead...
  [...range(1,10)].map(n => n**2) // [1, 4, 9, 16, 25, 36, 49, 64, 81]
  • Just a thought I had: I sort of wish that your error example worked like Object.entries, and in fact that Objects were iterable as entries the way that Maps are. I wish I could do stuff like this (but to be clear, this does NOT currently work):
  const o = {'a': 10, 'b': 20}
  [...o] // [["a", 10], ["b", 20]]
  for (const entry of o) console.log(entry)
  // ["a", 10]
  // ["b", 20]

Sigh... but I dream...

Collapse
 
julietter profile image
JulietteR

Thank you for this article!

One note--I noticed a small typo in the Merge Objects example.
The second object should be named obj2

let obj1 = {a: 1, b: 2, c: 3}
let obj1 = {d: 4, e: 5, f: 6}

let merge = {...obj1, ...obj2}
// merge is {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6}
Collapse
 
laurieontech profile image
Laurie

Thanks! I’ll fix that when I have a moment.

Collapse
 
qwtel profile image
Florian Klampfer

Missing my favourite use, which is to conditionally and declaratively add keys to objects, which isn't possible otherwise.

const obj = {
  a: 3,
  ...condition === true ? { b: 4 } : {},
}

Same for arrays

const plugins = [
  new BasicPlugin(),
  ...env === 'production' ? [new ProductionPlugin()] : [],
];
Collapse
 
genocideaon profile image
Pranat

What is the way you always use to prevent error when data is null or undefined?
For my case, I always face this when 'initVar' is null or undefined. Or at lease 'aaa' is null or undefined.

const {aaa: {bbb, ccc}} = initVar

Collapse
 
docx profile image
Lukáš Doležal • Edited

Maybe you could actually use the spread to start with default values and override with initVar:

const { aaa: { bbb, ccc } } = {
  aaa: { 
    bbb: "default bbb", 
    ccc: "default ccc", 
    ...(initVar && initVar.aaa) 
  }
}

But not sure if that is really useful in maybe more complex deconstructions? :D

Collapse
 
laurieontech profile image
Laurie

So that example is desrructhring assignment. I look at that in a different post which is linked at the bottom of this one.

Collapse
 
nataliedeweerd profile image
𝐍𝐚𝐭𝐚𝐥𝐢𝐞 𝐝𝐞 𝐖𝐞𝐞𝐫𝐝 • Edited

Copying an array

Why would you copy an array like this, rather than let copy = arr ?

Collapse
 
laurieontech profile image
Laurie • Edited

Because of it being a shallow copy.

let copy = [1, 2, 3]
let arr = copy
copy.push(4)
// copy is [1,2,3,4]
// arr is [1,2,3,4]

The spread operator would mean this

let copy = [1, 2, 3]
let arr = [...copy]
copy.push(4)
// copy is [1,2,3,4]
// arr is [1,2,3]

However, this only works for flattened arrays. Multidimensional arrays will be deep copies at only the top level.

Collapse
 
seanmclem profile image
Seanmclem

It does a deep copy for an array only, but does a shallow copy when used on an object. Is that right? Doesn't mention it in your article

Thread Thread
 
laurieontech profile image
Laurie

That is correct. And yes, it's something that I likely should have mentioned!

Collapse
 
nataliedeweerd profile image
𝐍𝐚𝐭𝐚𝐥𝐢𝐞 𝐝𝐞 𝐖𝐞𝐞𝐫𝐝

Thank you for explaining! :)

Collapse
 
jacobmgevans profile image
Jacob Evans

I would love some real-world use cases for Rest as well. I rarely use it but I feel like thats because I lack enough understanding. Same goes with Switch though, I need to just use them more lol

Collapse
 
kenbellows profile image
Ken Bellows

I find rest parameters super useful for writing wrapper functions. For a super simple example, I often like to define small logger functions as wrappers around console.log() that always add certain context, such as a label that tells me where the log was issued:

const log = (...args) => console.log('MyFile.js ::', ...args)

// ... later
const payload = {
  name: 'Ken',
  job: 'Web dev'
}
log('Sending:', payload)
// => MyFile.js :: Sending: { "name": "Ken", "job": "Web dev" }

This is a pretty trivial example, but I've used it for much more complicated cases where I basically always use the same default arguments for calls to library functions with lots of arguments, and I want a wrapper that supplies all but the last couple arguments for me. I'll write a wrapper that gathers arguments into a rest array, then spreads them into the end of my call to the library function, like I did with console.log above

Collapse
 
jacobmgevans profile image
Jacob Evans

This is super awesome. I can see the value already! Thank you so much! 😁

Collapse
 
laurieontech profile image
Laurie

Noted! Maybe I'll get to those in the future.

Collapse
 
jacobmgevans profile image
Jacob Evans

Awesome! Thank you for listening to my suggestion!

Some comments may only be visible to logged-in visitors. Sign in to view all comments.