Spread operator is used to access all elements inside of an iterable. Don't worry if the definition is not clear, we'll explain the operator in detail later on.
Iterable object is a computer science-y term for a category of data type: arrays, objects literals, and strings.
Spread operator's syntax is three dots ...
, which expands an iterable to the list of parameters.
Let's take a look at some examples:
const numbers = [1, 2, 3, 5];
const numbersCopy = [...numbers];
console.log(numbersCopy); // Prints "[1, 2, 3, 5]"
const user = {
name: "John",
surname: "Doe",
age: 18,
};
const userCopy = {...user};
// Prints { name: "John", surname: "Doe", age: 18 }
console.log(userCopy);
const string = "string";
// Converts string to array
const array = [...string];
console.log(array); // Prints ["s", "t", "r", "i", "n", "g" ] ha
const string = "string";
// Converts array to object
const object = {...string};
// Prints {"0": "s", "1": "t", "2": "r", "3": "i", "4": "n", "5": "g"}
console.log(object);
Important note: the spread operator does a shallow copy of an iterable.
Let's see the most popular use cases of the spread operator:
const parents = ["John", "Andrew"];
const children = ["Ann", "Rose"];
const humans = [...parents, ...children];
console.log(humans); // Prints ["John", "Andrew", "Ann", "Rose"]
Now, humans
array contains both parents
and children
.
The spread operator preserves the order in which the items were spread:
const parents = ["John", "Andrew"];
const children = ["Ann", "Rose"];
const humans = [...children, ...parents];
console.log(humans); // Prints ["Ann", "Rose", "John", "Andrew"]
Merging objects is very similar to merging arrays, but be aware that duplicate keys are overwritten:
const positiveNumbers = {
one: 1,
two: 2,
};
const negativeNumbers = {
minusOne: -1,
minusTwo: -2,
};
const numbers = {
...negativeNumbers,
...positiveNumbers,
};
// Prints "{ minusOne: -1, minusTwo: -2, one: 1, two: 2 }"
console.log(numbers);
Overriding duplicate keys:
const user = {
name: "John",
surname: "Doe",
nickname: "johndoe",
};
const updatedUser = {
name: "Andrew",
surname: "Hopkins",
nickname: "andrewhopkins",
};
const newUser = {
...user,
...updatedUser,
};
// Prints {name: "Andrew", surname: "Hopkins", nickname: "andrewhopkins"}
console.log(newUser);
Note, how in the example above all keys were overwritten with the new values.
This is encountered not that often, but still worth remembering about:
const add = (number1, number2, number3) => number1 + number2 + number3;
const numbers = [1, 2, 3];
// Spreads array of numbers to a function arguments
const result = add(...numbers);
console.log(result); // Prints "6"
Sometimes there is a need to update object property without mutating it. The spread operator offers great help in this case, let's see:
const user = {
name: "John",
surname: "Doe",
age: 18,
};
const newUser = {
...user,
age: 22,
};
// Prints {name: "John", surname: "Doe", age: 18}
console.log(user);
// Prints {name: "John", surname: "Doe", age: 22}
console.log(newUser);
Note, how the original object user
has not been mutated.
Important note: always remember that objects are copied by reference:
const user = {
name: "John",
surname: "Doe",
age: 18,
};
const newUser = user;
newUser.age = 22;
// Prints {name: "John", surname: "Doe", age: 22}
console.log(user);
// Prints {name: "John", surname: "Doe", age: 22}
console.log(newUser);
Remember us, saying that spread operator does a shallow copy of an iterable?
Shallow copy is a bit-wise copy of an object. A new object is created that has an exact copy of the values in the original object. If any of the fields of the object are references to other objects, just the reference addresses are copied i.e., only the memory address is copied.
Consider the following example:
const user = {
name: "John",
surname: "Doe",
other: {
age: 18,
},
};
const newUser = {
...user,
};
newUser.other.age = 22;
// Prints {name: "John", surname: "Doe", age: 22}
console.log(user);
// Prints {name: "John", surname: "Doe", age: 22}
console.log(newUser);
Property other
references to an object which contains age
. When doing shallow copy by using the spread operator, just the reference address of other
is copied, not the value itself.
That's why when we modify other.age
it gets updated in both user
and newUser
.
The rest pattern syntax allows us to represent an indefinite number of arguments as an array:
const numbers = [1, 2, 3, 5, 8];
const [firstNumber, ...rest] = numbers;
console.log(firstNumber); // Prints "1"
console.log(rest); // Prints "[2, 3, 5, 8]"
In the example above, the rest pattern collected all elements but the first one into an array.
Spread operator unpacks, rest packs:
const user = {
name: "John",
surname: "Doe",
age: 18,
};
// Pack all remaining elements into new object
const {name, ...rest} = user;
// Unpack all props of user into newUser
const newUser = {
...user,
};
...