Understanding Shallow Copy vs Deep Copy in JavaScript with Custom Implementations

Introduction

When working with JavaScript, understanding the difference between shallow copy and deep copy is essential for handling object replication correctly. Before diving into these copying mechanisms, let's first examine the two categories of data types in JavaScript:

  • Primitive Types (7 types): String, Number, Boolean, null, undefined, Symbol, BigInt
  • Reference Types: Object (including Array, Function, RegExp, Date, etc.)

How Data is Stored in Memory

Primitive types are stored directly in the stack as simple data segments. They can be accessed and manipulated by their direct values. Reference types, how ever, are stored in the heap memory. The stack contains a pointer (reference address) that points to the actual object in heap memory. This pointer allows JavaScript to locate and access the object efficiently.

Shallow Copy

What is a Shallow Copy?

A shallow copy creates a new data structure that contains references to the same memory addresses as the original object for nested reference types. In other words:

  • For primitive types, the actual value is copied
  • For reference types, only the memory address is copied, meaning both the original and copied objects share the same nested objects

Methods to Create Shallow Copies

1. Using Object.assign()

const source = {
    age: 25,
    profile: {
        firstName: 'alice',
        lastName: 'bob'
    },
    tags: ['developer', 'engineer'],
    greet: function() {
        console.log('hello');
    }
};

const target = Object.assign({}, source);

// Since it's a shallow copy, only primitive values are truly copied
// Reference types still share the same memory address
source.profile.firstName = 'changed';
source.tags[0] = 'modified';

console.log(target.profile.firstName); // Output: changed

2. Using Array.slice()

const originalArray = ["First", {
    title: "Second",
    version: 3
}, "Third"];

const copiedArray = originalArray.slice(0);
originalArray[1].title = "Fourth";

console.log(copiedArray[1].title); // Output: Fourth

3. Using Array.concat()

const originalArray = ["First", {
    title: "Second",
    version: 3
}, "Third"];

const copiedArray = originalArray.concat();
originalArray[1].title = "Fourth";

console.log(copiedArray[1].title); // Output: Fourth

4. Using Spread Operator

const originalArray = ["First", {
    title: "Second",
    version: 3
}, "Third"];

const copiedArray = [...originalArray];
originalArray[1].title = "Fourth";

console.log(copiedArray[1].title); // Output: Fourth

Deep Copy

What is a Deep Copy?

A deep copy creates a completely independent copy of an object, including all nested objects. Changes made to the copied object will not affect the original object, and vice versa. This is achieved by allocating new memory space for all properties, including nested reference types.

Methods to Create Deep Copies

1. Using JSON.parse() and JSON.stringify()

const source = {
    age: 25,
    profile: {
        firstName: 'alice',
        lastName: 'bob'
    },
    tags: ['developer', 'engineer'],
    greet: function() {
        console.log('hello');
    }
};

const target = JSON.parse(JSON.stringify(source));

source.profile.firstName = 'modified';
console.log(target.profile.firstName); // Output: alice

Important Limitations:

  • Properties with undefined values are ignored
  • Function properties are ignored
  • Symbol keys are not preserved
  • Non-circular references are handled correctly
  • Performance may degrade with large data structures

Custom Implementation of Shallow and Deep Copy

Custom Shallow Copy Function

function shallowCopy(original) {
    const result = {};
    for (const property in original) {
        if (Object.prototype.hasOwnProperty.call(original, property)) {
            result[property] = original[property];
        }
    }
    return result;
}

Testing the shallow copy:

const source = {
    age: 25,
    profile: {
        firstName: 'alice',
        lastName: 'bob'
    },
    tags: ['developer', 'engineer'],
    greet: function() {
        console.log('hello');
    }
};

const copy = shallowCopy(source);
console.log(source);
console.log(copy);

Custom Deep Copy Function

function deepClone(original, cache = new WeakMap()) {
    // Handle null and undefined
    if (original === null) return original;
    
    // Handle Date objects
    if (original instanceof Date) {
        return new Date(original.getTime());
    }
    
    // Handle RegExp objects
    if (original instanceof RegExp) {
        return new RegExp(original.source, original.flags);
    }
    
    // For primitive types and functions, return as-is
    if (typeof original !== 'object') {
        return original;
    }
    
    // Check cache to handle circular references
    if (cache.get(original)) {
        return cache.get(original);
    }
    
    // Create new object of the same type
    const cloneTarget = new original.constructor();
    cache.set(original, cloneTarget);
    
    // Recursively copy all properties
    for (const key in original) {
        if (Object.prototype.hasOwnProperty.call(original, key)) {
            cloneTarget[key] = deepClone(original[key], cache);
        }
    }
    
    return cloneTarget;
}

Testing the deep copy:

const source = {
    age: 25,
    profile: {
        firstName: 'alice',
        lastName: 'bob'
    },
    tags: ['developer', 'engineer'],
    greet: function() {
        console.log('hello');
    }
};

const deepCopyResult = deepClone(source);
const shallowCopyResult = Object.assign({}, source);
const customShallowCopy = shallowCopy(source);

source.profile.firstName = 'modified';

// Deep copy creates new memory, so original change doesn't affect the copy
console.log(deepCopyResult.profile.firstName); // Output: alice

// Shallow copies share reference types with the original
console.log(shallowCopyResult.profile.firstName); // Output: modified
console.log(customShallowCopy.profile.firstName); // Output: modified

Tags: javascript copy deep-copy shallow-copy memory-management

Posted on Sat, 09 May 2026 01:06:41 +0000 by PascalNouma