The Spread operator

Javascript has an important operator, the 'Spread' operator: ..., which work with arrays and objects.

Collections (arrays and objects)

The array and object types are 'collections'. Since they can contain many other types, including themselves!

Modifying Collections (avoid!)

We already covered how define an arrays e.g.: [1, 2, 3] and objects e.g. {hello: 'world'}. But what if we want to modify them?

Example:

const a = [1, 2, 3]

Here we've defined an array a. How can we modify a to add 4 to the end, like this: [1, 2, 3, 4]?

In javascript there are special functions on arrays to help you do this.

const a = [1, 2, 3]
a.push(4)
console.log(a)
// prints: [1, 2, 3, 4]

a.push(4) modifies a. We call this mutation.

Mutation is not recommended because it is harder for you to reason about. It's not considered best practice.

In the example above even though a is a const (and cannot be reassigned) it's value is different in two different points in your program:

const a = [1, 2, 3]
console.log(a)
// prints: [1, 2, 3]
a.push(4)
console.log(a)
// prints: [1, 2, 3, 4]

You can also mutate objects like this:

const o = {hello: 'world'}
o.hello = 'alice'
console.log(o)
// prints: {hello: 'alice'}

Try to avoid mutation where you can! [1]

Spread - avoiding mutation

The spread operator is ... (three dots!).

Array Spread

Let's look at how we can add 4 to [1, 2, 3] like before:

const a = [1, 2, 3]
const a1 = [...a, 4]
console.log(a)
// prints: [1, 2, 3]
console.log(a1)
// prints: [1, 2, 3, 4]

In this example we spread in a into a new array a1.

a remains unchanged (no mutation) and a1 is [1, 2, 3, 4].

The ... operator clones (creates a copy) of the collection [1].

Here are some more examples with arrays:

const array = [1, 2, 3]

[...array]
// => [1, 2, 3] cloned!

[0, ...array]
// => [0, 1, 2, 3]

[...array, 4]
// => [1, 2, 3, 4]

[0, ...array, 4]
// => [0, 1, 2, 3, 4]

[0, ...array.slice(0, 2), 4]
// => [0, 1, 2, 4] - array.slice(0, 2) returns a copy of the array with the 0th to 1st indexes included: [1, 2]

Object Spread

Spreading also works with objects:

const o = {ada: 'lovelace', hello: 'world', }
const o1 = {...o, hello: 'alice'}

console.log(o)
// prints: {ada: 'lovelace', hello: 'world', }
console.log(o1)
// prints: {ada: 'lovelace', hello: 'alice', }

Just like with arrays the object is 'cloned' and the original remains unchanged.

If you have the same key twice in an object, the last (rightmost) one wins.

Example:

{a: 1, b: 2, a: 3}
// => {a: 3, b: 2} - a is 3 not 1

Here are some more spread examples with objects:

const object = {a: 1, b: 2}

{...object}
// => {a: 1, b: 2} cloned!

{...object, c: 3}
// => {a: 1, b: 2, c: 3}

{c: 3, ...object}
// => {a: 1, b: 2, c: 3} - object's don't actually have an 'order' (arrays do though!)

{...object, b: 3} // => {a: 1, b: 3}

{b: 3, ...object}
// => {a: 1, b: 2} - this is like writing: {b: 3, a: 1, b: 2}, the last key: b: 2 wins and b: 3 is ignored.

{object: object}
// => {object: {a: 1, b: 2}}

{object}
// => {object: {a: 1, b: 2}} (shorthand for one above)

{object: {...object, c: 3}}
// => {object: {a: 1, b: 2, c: 3}}

[1] Sometimes mutating has better performance than 'cloning' / spreading. But it's very rare it'll make a difference to you, especially since you're just starting out!