Learn how to copy arrays in JavaScript using shallow and deep copy techniques. Understand the differences between slice(), spread operator, and deep copy methods.
Arrays are a fundamental data structure in JavaScript, allowing us to store and manipulate collections of data. As we work with arrays, we often need to copy them to preserve the original data or to manipulate the copy without affecting the original. In this section, we’ll explore how to copy arrays in JavaScript, focusing on both shallow and deep copy techniques. We’ll also discuss the differences between these methods and provide examples to illustrate their use.
A shallow copy of an array is a new array that contains references to the same elements as the original array. If the elements are primitive values (like numbers or strings), the shallow copy will have its own copy of those values. However, if the elements are objects or other arrays, the shallow copy will only contain references to those objects or arrays, not copies of them.
slice()
for Shallow CopiesThe slice()
method is a built-in JavaScript function that returns a shallow copy of a portion of an array into a new array object. It does not modify the original array. Let’s see how it works:
// Original array
const originalArray = [1, 2, 3, 4, 5];
// Shallow copy using slice()
const shallowCopy = originalArray.slice();
console.log(shallowCopy); // Output: [1, 2, 3, 4, 5]
// Modifying the shallow copy
shallowCopy[0] = 10;
console.log(originalArray); // Output: [1, 2, 3, 4, 5]
console.log(shallowCopy); // Output: [10, 2, 3, 4, 5]
In this example, the slice()
method creates a shallow copy of originalArray
. When we modify the shallowCopy
, the originalArray
remains unchanged, demonstrating that the copy is independent for primitive values.
[...array]
The spread operator (...
) is another way to create a shallow copy of an array. It expands the elements of an array into a list of elements. Here’s how you can use it:
// Original array
const originalArray = [1, 2, 3, 4, 5];
// Shallow copy using spread operator
const shallowCopy = [...originalArray];
console.log(shallowCopy); // Output: [1, 2, 3, 4, 5]
// Modifying the shallow copy
shallowCopy[0] = 10;
console.log(originalArray); // Output: [1, 2, 3, 4, 5]
console.log(shallowCopy); // Output: [10, 2, 3, 4, 5]
The spread operator provides a concise and modern way to create shallow copies of arrays, especially when working with ES6 and beyond.
While shallow copies work well for arrays containing primitive values, they have limitations when dealing with nested arrays or objects. Let’s explore this with an example:
// Original array with nested objects
const originalArray = [{ a: 1 }, { b: 2 }, { c: 3 }];
// Shallow copy using slice()
const shallowCopy = originalArray.slice();
// Modifying a nested object in the shallow copy
shallowCopy[0].a = 10;
console.log(originalArray); // Output: [{ a: 10 }, { b: 2 }, { c: 3 }]
console.log(shallowCopy); // Output: [{ a: 10 }, { b: 2 }, { c: 3 }]
In this case, both originalArray
and shallowCopy
reflect the change because the shallow copy only copies references to the nested objects, not the objects themselves.
A deep copy of an array creates a new array with copies of all the elements, including nested objects or arrays. This ensures that changes to the copied array do not affect the original array. Deep copying is more complex than shallow copying, but it’s essential when working with nested structures.
One common method for deep copying arrays is to use JSON serialization and deserialization. This method works well for arrays containing serializable data (i.e., data that can be converted to JSON):
// Original array with nested objects
const originalArray = [{ a: 1 }, { b: 2 }, { c: 3 }];
// Deep copy using JSON methods
const deepCopy = JSON.parse(JSON.stringify(originalArray));
// Modifying a nested object in the deep copy
deepCopy[0].a = 10;
console.log(originalArray); // Output: [{ a: 1 }, { b: 2 }, { c: 3 }]
console.log(deepCopy); // Output: [{ a: 10 }, { b: 2 }, { c: 3 }]
In this example, the JSON.stringify()
method converts the array to a JSON string, and JSON.parse()
converts it back to a new array, effectively creating a deep copy.
While JSON methods are convenient, they have limitations:
undefined
, and other non-serializable data types cannot be copied using JSON methods.Map
, Set
, and other special object types are not preserved when using JSON methods.For more complex data structures, you may need to implement a custom deep copy function. Here’s a simple example:
function deepCopyArray(arr) {
return arr.map(item => {
if (Array.isArray(item)) {
return deepCopyArray(item);
} else if (item && typeof item === 'object') {
return deepCopyObject(item);
} else {
return item;
}
});
}
function deepCopyObject(obj) {
const copy = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
if (Array.isArray(obj[key])) {
copy[key] = deepCopyArray(obj[key]);
} else if (obj[key] && typeof obj[key] === 'object') {
copy[key] = deepCopyObject(obj[key]);
} else {
copy[key] = obj[key];
}
}
}
return copy;
}
// Original array with nested objects and arrays
const originalArray = [{ a: 1, b: [2, 3] }, { c: 4 }];
// Deep copy using custom function
const deepCopy = deepCopyArray(originalArray);
// Modifying a nested object in the deep copy
deepCopy[0].b[0] = 10;
console.log(originalArray); // Output: [{ a: 1, b: [2, 3] }, { c: 4 }]
console.log(deepCopy); // Output: [{ a: 1, b: [10, 3] }, { c: 4 }]
This custom function recursively copies arrays and objects, ensuring a true deep copy.
To better understand the differences between shallow and deep copies, let’s visualize the concept using a diagram.
graph TD; A[Original Array] --> B[Shallow Copy] A --> C[Deep Copy] B --> D[References to Original Elements] C --> E[Copies of Original Elements]
Diagram Explanation:
Now that we’ve explored both shallow and deep copying techniques, let’s try modifying the examples to reinforce your understanding:
Experiment with Nested Arrays: Create an array with nested arrays and try copying it using both shallow and deep copy methods. Observe the differences in behavior.
Test with Different Data Types: Include different data types, such as functions and undefined
, in your arrays to see how JSON methods handle them.
Implement Custom Copy Functions: Write your own custom deep copy function and test it with complex data structures.
slice()
or the spread operator (...
) for shallow copies. They work well for arrays with primitive values but only copy references for nested objects or arrays.For more information on array copying techniques, consider exploring the following resources: