Javascript has an important operator, the 'Spread' operator: ...
, which work with arrays and objects.
The array and object types are 'collections'. Since they can contain many other types, including themselves!
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]
The spread operator is ...
(three dots!).
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]
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!