When you need a javascript array copy or js array copy—to sort, dedupe, or mutate without touching the source—most snippets only perform a shallow duplicate. That is fine for primitives (numbers, strings, booleans): the new array holds its own values, so changing the original’s slots does not rewrite the copy. For objects or nested arrays, shallow methods reuse the same inner references, so a javascript clone array plan must add deep copy tooling when you need true isolation (javascript deep copy array vs javascript shallow copy array).
Tested on: Node.js v20.18.2. A short note after each runnable snippet describes what you should see in the console.
Quick reference
New outer array + same inner references = shallow; structuredClone duplicates nested plain data when types are supported.
| Approach | Typical use |
|---|---|
[...arr], arr.slice(), Array.from(arr) |
Shallow javascript array copy for primitives or when shared objects are intentional |
map / filter |
New array because you are transforming or subsetting, not only duplicating |
structuredClone(arr) |
Deep tree of structured-cloneable data (javascript deep copy array) |
JSON.parse(JSON.stringify(...)) |
JSON-only payloads; know the limitations before you rely on it for javascript clone array trees |
1. Spread operator (...)
The spread operator is the most readable copy array javascript pattern for dense arrays: const copy = [...original] creates a new array and copies enumerable elements one level. It is the default people mean by javascript copy array in modern codebases.
const originalArray = [1, 2, 3];
const copiedArray = [...originalArray];
console.log(copiedArray);
originalArray[0] = 10;
originalArray[1] = 20;
originalArray[2] = 30;
console.log(copiedArray);
console.log(originalArray);
console.log(originalArray === copiedArray);You should see four lines: two times [ 1, 2, 3 ], then [ 10, 20, 30 ], then false (the copy keeps primitive values; === confirms different array objects).
2. Array.prototype.slice()
Calling slice() with no arguments is the classic array copy javascript idiom: it returns a new array with shallow copies of the elements in the same order. Behavior on sparse arrays differs from spread—see the sparse section later and the FAQ.
const originalArray = [1, 2, 3];
const copiedArray = originalArray.slice();
console.log(copiedArray);
originalArray[0] = 3;
originalArray[1] = 4;
originalArray[2] = 6;
console.log(copiedArray);
console.log(originalArray);You should see three lines: [ 1, 2, 3 ] twice, then [ 3, 4, 6 ] for the mutated original while the slice copy stays [ 1, 2, 3 ].
3. Array.from()
Array.from materializes a new array from an iterable or array-like value—another explicit javascript array copy when you want a real Array instance (for example from a NodeList) or a predictable materialization step in copy array js utilities.
const originalArray = [1, 2, 3];
const copiedArray = Array.from(originalArray);
console.log(copiedArray);You should see one line: [ 1, 2, 3 ].
4. Array.prototype.concat()
concat with an empty array (or the pattern below) yields a new array with the same shallow references as the source—useful when a pipeline already ends in concat or you prefer a non-spread style for js array copy in older style guides.
const originalArray = [4, 5, 6];
const copiedArray = [].concat(originalArray);
console.log(copiedArray);
originalArray[0] = 90;
originalArray[1] = 91;
originalArray[2] = 92;
console.log(copiedArray);You should see two lines: [ 4, 5, 6 ] twice (primitives copied by value into new slots).
5. Array.prototype.map()
map((x) => x) returns a new array and runs your callback per index. For primitives it behaves like other shallow copies; for sparse arrays it is not interchangeable with slice without reading the caveats in the FAQ—prefer spread or slice for a plain javascript shallow copy array unless you are transforming values.
const originalArray = [1, 2, 3];
const copiedArray = originalArray.map((x) => x);
console.log(copiedArray);
originalArray[0] = 100;
console.log(copiedArray);You should see two lines, both [ 1, 2, 3 ] (the mapped copy does not pick up the later mutation of index 0 because values were copied at map time).
6. Array.prototype.filter()
filter returns a new array containing only elements that pass your test—here, even numbers. Surviving entries are still shallow copies of references: object elements point at the same instances as in the source, which matters when you chain transforms after copying.
const originalArray = [1, 2, 3, 4];
const copiedArray = originalArray.filter((x) => x % 2 === 0);
console.log(copiedArray);You should see one line: [ 2, 4 ].
Shallow copies and nested objects
For objects inside the array, spread, slice, Array.from, map, filter, and concat only copy the outer array container. Inner objects stay shared—both the original and the copy array js view the same mutation unless you deep clone.
const nested = [{ x: 1 }, { x: 2 }];
const shallowCopy = [...nested];
nested[0].x = 99;
console.log(nested);
console.log(shallowCopy);You should see two identical lines: both arrays show { x: 99 } in the first slot.
Deep copy with structuredClone
For a true javascript deep copy array of plain nested data, structuredClone duplicates nested objects and arrays without the JSON limitations (MDN). It still cannot clone everything (some platform objects or prototypes); when you hit those edges, use libraries or domain-specific logic. A broader object-oriented discussion lives in the JavaScript deep copy guide.
const nested = [{ x: 1 }, { x: 2 }];
const deepCopy = structuredClone(nested);
nested[0].x = 99;
console.log(nested);
console.log(deepCopy);You should see two lines: the first row shows x: 99, the deep copy still shows x: 1 for the first object.
Deep copy with JSON.parse(JSON.stringify(...))
JSON serialization is a common hack for javascript clone array trees of JSON-safe data. It drops undefined, functions, Symbol keys, circular graphs, and weakens Date and Map/Set fidelity—so treat it as a narrow tool, not a general deep cloner for copy an array javascript workflows that include rich types.
const originalArray = [1, 2, [3, 4], { a: 5, b: 6 }];
const copiedArray = JSON.parse(JSON.stringify(originalArray));
console.log(copiedArray);You should see one line: [ 1, 2, [ 3, 4 ], { a: 5, b: 6 } ].
Sparse arrays: spread vs slice vs map
Sparse arrays (arrays with “holes”) behave differently depending on which copy API you pick. On Node v20, spread fills a hole with undefined, while slice preserves the hole, and map skips holes but keeps them empty in the result:
const sparse = [1, , 3];
console.log([...sparse]);
console.log(sparse.slice());
console.log(sparse.map((x) => x));You should see three lines: spread shows undefined in the hole; slice and map may show <empty item> style holes in Node’s formatter (see console for exact wording).
Summary
Shallow tools give a new array but shared nested objects; structuredClone is the native deep path for structured-cloneable trees; JSON is a narrow hack.
- Shallow javascript copy array tools (
[...arr],slice,from,concat) give a new outer array but share nested object references—fine for primitives, risky for nested state. - Use
map/filterwhen the goal is a derived array, not only a duplicate; check sparse-array semantics againstslice/spread. - Prefer
structuredClonefor javascript deep copy array graphs of structured-cloneable plain data. - Reserve JSON round-trip for JSON-only payloads; it is not a full javascript clone array solution.
- On sparse arrays, verify array copy javascript behavior (holes vs
undefined) before shipping.
References
MDN and on-site guides for spread, slice, structuredClone, and the wider deep-copy topic behind copy array javascript examples.
- MDN: Spread syntax
- MDN:
Array.prototype.slice - MDN:
structuredClone - JavaScript deep copy on this site
Frequently Asked Questions
1. What is the best way to copy an array in JavaScript?
[...arr]) or slice() are common and readable. Use structuredClone(arr) when you need a deep copy of nested objects and arrays (where supported). Avoid JSON.parse(JSON.stringify(...)) as a general deep clone—it drops functions, undefined, special numeric values, prototype chains, circular references, and more.2. Does copying an array copy the objects inside it?
slice, Array.from, map, filter, and concat create a new array but still reference the same inner objects. Mutating a nested object in one array is visible through the other unless you deep clone.3. Is `map((x) => x)` the same as copying an array?
slice() or spread (holes vs undefined). Prefer spread or slice for a plain shallow copy unless you are transforming values.4. Do spread and slice behave the same on sparse arrays?
undefined in the new array, while slice can preserve holes. map skips holes but preserves them in the result. Prefer explicit Array.from with a mapper or document the behavior you rely on.