London, Ontario
5 min read Updated
JavaScript — Using The Spread Operator (…)
A simplified look at how to use the Spread Operator (…) in JavaScript: in function calls, arrays and objects, and why the copies it makes are shallow.
The spread operator is three dots, ..., that open up an array, a string or an object so its values can go somewhere new: into a function call, a new array, or a new object. It’s one of those bits of syntax that looks cryptic at first and then turns up everywhere once you know it. By the end of this post you’ll know the three places it works, and the one catch that trips people up.
For a function call
Put ... in front of an array when you call a function, and each element becomes its own argument:
const numbers = [1, 2, 3];
Math.max(...numbers); // 3, the same as Math.max(1, 2, 3)
The same three dots in a function’s parameters do the opposite job. There they’re called rest parameters, and they gather any number of arguments into one array:
const sum = (...nums) => {
// `nums` is an array of whatever was passed in, like [1, 2, 3]
let total = 0;
for (const n of nums) {
total = total + n;
}
return total;
};
sum(1); // 1
sum(1, 2, 3); // 6
sum(1, 2, 3, 4, 5); // 15
sum(...numbers); // 6: spread into a function that collects with rest
A quick way to tell them apart: in a call, ... spreads values out; in a parameter list, it gathers them in.
For an array
Inside an array literal, spread copies the elements of another array into the new one. That makes it easy to add items or join arrays together:
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const arr3 = [...arr1, "hello"]; // [1, 2, 3, "hello"]
const arr4 = ["hello", ...arr2]; // ["hello", 4, 5, 6]
const arr5 = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
const arr6 = [...arr1, arr2]; // [1, 2, 3, [4, 5, 6]]
Notice the last one: without the dots, arr2 goes in as a single element, an array inside the array.
It also works on anything iterable, so a string spreads into its characters: [..."hi"] is ["h", "i"].
For an object
In an object literal, spread copies another object’s properties. Use it to merge objects:
const obj1 = { 1: "a", 2: "b" };
const obj2 = { 3: "c", 4: "d" };
const obj3 = { ...obj1, ...obj2 }; // { 1: "a", 2: "b", 3: "c", 4: "d" }
If two objects share a property, the one that comes later wins. That’s how you update a property without changing the original object:
const user = { name: "Waqar", city: "London" };
const moved = { ...user, city: "Toronto" };
// moved: { name: "Waqar", city: "Toronto" }
// user is unchanged: { name: "Waqar", city: "London" }
Copying, and the catch
Assigning an array to a new variable doesn’t copy it. Both names point at the same array:
const arr = [1, 2, 3];
const same = arr; // not a copy: the same array under another name
const copy = [...arr]; // a new array
arr.push(4);
same; // [1, 2, 3, 4]
copy; // [1, 2, 3]
The catch is that spread makes a shallow copy. The outer array (or object) is new, but anything nested inside it is still shared:
const nested = [[1, 2], [3, 4]];
const shallow = [...nested];
nested[0].push(99);
shallow; // [[1, 2, 99], [3, 4]]: the inner arrays are shared
When you need a full, deep copy, reach for structuredClone, which is built into modern browsers and Node:
const deep = structuredClone(nested); // nothing shared
Conclusion
The spread operator is mostly syntactic sugar, but it makes everyday code shorter and easier to read: passing arrays as arguments, building arrays, and merging or updating objects without changing the originals. Remember the two things that catch people out: ... in a parameter list gathers (rest), and spread copies are shallow.
Updated in 2026: the original version of this post called spread copies “deep”; they’re shallow. It also used rest parameters for the function example, and the code is now text rather than screenshots.
Waqar