Last modified: Jul 31, 2026
JavaScript Copy Array: Shallow vs Deep
Copying arrays in JavaScript seems simple. But it hides a common trap. If you copy an array the wrong way, you might change the original data by accident. This guide explains the difference between shallow and deep copies. You will learn the best methods for each situation. Let's dive in and master array copying today.
Why Copying Arrays Matters
Arrays in JavaScript are reference types. When you assign an array to a new variable, you don't create a new copy. You just create a new reference to the same array in memory. This means changes to one variable affect the other. This is often not what you want. Understanding this is the first step to safe data handling.
Consider this basic example. You have a list of tasks. You want to create a backup before modifying it. If you use a simple assignment, your backup will change too. This can lead to bugs that are hard to find. You need a proper copy method to avoid these issues.
// Assignment creates a reference, not a copy
let original = [1, 2, 3];
let wrongCopy = original;
wrongCopy.push(4);
console.log(original); // Output: [1, 2, 3, 4] - Original is modified!
console.log(wrongCopy); // Output: [1, 2, 3, 4]
What is a Shallow Copy?
A shallow copy creates a new array. The top-level elements are copied. However, if the array contains nested objects or arrays, only the references to those nested items are copied. The nested items themselves are not duplicated. This means changes to nested objects in the copy will also affect the original.
Think of it like copying a shopping list. You write down the same items on a new piece of paper. But if one item is a box of chocolates, you don't open the box and copy each chocolate. You just write "box of chocolates" on the new list. If someone eats a chocolate from the box, both lists are now wrong.
For most simple arrays with primitive values (numbers, strings, booleans), a shallow copy is perfectly fine. It gives you an independent array. The issues only arise with multi-dimensional arrays or arrays of objects.
Methods for Shallow Copy
There are several built-in methods to create a shallow copy. The most common ones are the spread operator, Array.from(), and the slice() method. Each is easy to use. Let's look at them with examples.
// Using the spread operator
let arr1 = [1, 2, 3];
let copy1 = [...arr1];
copy1.push(4);
console.log(arr1); // Output: [1, 2, 3] - Original is safe
console.log(copy1); // Output: [1, 2, 3, 4]
// Using Array.from()
let arr2 = [4, 5, 6];
let copy2 = Array.from(arr2);
copy2[0] = 100;
console.log(arr2); // Output: [4, 5, 6] - Original is safe
console.log(copy2); // Output: [100, 5, 6]
// Using slice()
let arr3 = [7, 8, 9];
let copy3 = arr3.slice();
copy3.pop();
console.log(arr3); // Output: [7, 8, 9] - Original is safe
console.log(copy3); // Output: [7, 8]
What is a Deep Copy?
A deep copy creates a new array. It also creates new copies of all nested objects and arrays inside it. The copy is completely independent of the original. Changes to any part of the deep copy will not affect the original array. This is the safest way to copy complex data structures.
Returning to our shopping list analogy, a deep copy means you not only write down "box of chocolates" on the new list, but you also open the box, copy each individual chocolate, and place them in a new box. Now, if someone eats a chocolate from the new box, the original box is untouched.
Deep copying is essential when you work with data from APIs or complex state management systems. It prevents accidental mutations that can cause unpredictable behavior in your application. It's a crucial skill for any serious JavaScript developer.
Methods for Deep Copy
For a reliable deep copy, the modern standard is structuredClone(). This is a built-in browser and Node.js function. It handles all data types, including Dates and Maps. Another common method is using JSON.parse() and JSON.stringify(). However, this method has limitations. It does not work with functions, undefined, or Symbol values.
// Using structuredClone() - Modern and robust
let originalObj = { name: "Task", items: [1, 2, { id: 1 }] };
let deepCopy1 = structuredClone(originalObj);
deepCopy1.items[2].id = 99;
console.log(originalObj.items[2].id); // Output: 1 - Original is safe
console.log(deepCopy1.items[2].id); // Output: 99 - Copy is modified
// Using JSON methods - Simple but limited
let originalArr = [{ a: 1, b: 2 }];
let deepCopy2 = JSON.parse(JSON.stringify(originalArr));
deepCopy2[0].a = 100;
console.log(originalArr[0].a); // Output: 1 - Original is safe
console.log(deepCopy2[0].a); // Output: 100 - Copy is modified
Deep Copy vs Shallow Copy: Key Differences
The core difference lies in how nested data is handled. A shallow copy shares nested objects. A deep copy duplicates them. Making the right choice depends on your data structure and your needs. If you only have primitive values, a shallow copy is efficient and sufficient.
If you have nested arrays or objects, you must decide if you need to modify them. If you plan to update nested data in your copy, you need a deep copy. Using a shallow copy in this scenario will lead to bugs. Always assess your data complexity first.
Performance is another factor. Deep copying is slower because it recursively copies everything. For large or deeply nested data, this can impact performance. Use shallow copies when you can, and reserve deep copies for when they are truly necessary.
Common Pitfalls and How to Avoid Them
The most common pitfall is using the assignment operator = to copy an array. This is not a copy at all. It's just creating a new variable that points to the same array. This is the source of many bugs. Always use a method like the spread operator or slice() for a simple copy.
Another pitfall is assuming a shallow copy is a deep copy. You might use the spread operator on an array of objects. You then modify a property of an object in the new array. To your surprise, the original array's object changes too. This happens because the spread operator only copies the references to the objects.
To avoid these issues, test your copy methods. Create a small example with nested data. Modify the copy and check the original. This simple test will save you hours of debugging. Remember, structuredClone() is your best friend for deep copies.
Practical Examples and Use Cases
Let's look at a practical scenario. You are building a to-do app. You have an array of task objects. You want to allow the user to edit a task, but only save the changes if they click "Save". You need to work on a copy of the task object or the array.
If you use a shallow copy of the array, you can safely add or remove tasks. But if you edit a task's property, you will change the original. In this case, you need a deep copy of the task object you are editing. This ensures the user's changes are only temporary.
// Example: Editing a task in a to-do app
let tasks = [
{ id: 1, title: "Learn JS", completed: false },
{ id: 2, title: "Build a project", completed: false }
];
// User wants to edit task 1
let editingTask = structuredClone(tasks[0]); // Deep copy the single task
editingTask.title = "Learn Advanced JS";
console.log(tasks[0].title); // Output: "Learn JS" - Original is safe
console.log(editingTask.title); // Output: "Learn Advanced JS" - Copy is modified
This pattern is common in state management libraries like Redux. You create a copy of the state, modify the copy, and then dispatch the new state. This ensures that the original state is never mutated directly. This leads to more predictable and debuggable applications.
Best Practices for Copying Arrays
First, always use the spread operator [...arr] for a quick and simple shallow copy. It is concise and readable. For more robust scenarios, consider the slice() method. It is also fast and clear. Avoid the assignment operator for copying.
Second, for deep copies, always prefer structuredClone(). It is the standard, modern solution. It handles all data types correctly. If you need to support older browsers, you can use the JSON method, but be aware of its limitations. Do not write your own deep copy function unless you have a very specific need.
Third, think about your data. If you are working with an array of numbers or strings, a shallow copy is enough. If you have an array of objects or arrays, you likely need a deep copy. This simple mental check will guide your decision. For more on handling arrays of objects, see our JavaScript Array of Objects Guide.
Finally, remember that copying is about control. You want to control when data is shared and when it is independent. By mastering these techniques, you gain that control. This is a fundamental skill for writing clean, bug-free JavaScript. You can also review the JavaScript Array Methods Guide for more tools.
Conclusion
Copying arrays in JavaScript is a fundamental skill. You learned that simple assignment doesn't copy. It only creates a reference. You now understand the difference between shallow and deep copies. Shallow copies share nested data, while deep copies duplicate everything.
Use the spread operator or slice() for shallow copies of simple arrays. Use structuredClone() for deep copies of complex data. Always test your copy method to ensure it behaves as expected. This will prevent many common bugs.
Now you can handle arrays with confidence. You know how to protect your original data. This knowledge will make your code more robust and your debugging sessions shorter. For more on array properties, check out our JavaScript Array Length Guide. Start using these techniques in your projects today.