javascriptdeep-copyshallow-copy

Does Spread Syntax create a shallow copy or a deep copy?


I am extremely confused for days now regarding the true definition of a shallow copy and a deep copy.

When I read the mdn docs (https://developer.mozilla.org/en-US/docs/Glossary/Shallow_copy) on shallow copy, it all made sense. The first paragraph clearly explains what a shallow copy is. The docs says

A shallow copy of an object is a copy whose properties share the same references (point to the same underlying values) as those of the source object from which the copy was made. As a result, when you change either the source or the copy, you may also cause the other object to change too — and so, you may end up unintentionally causing changes to the source or copy that you don't expect.

I totally got that part. From my understanding, the below code example is the example of a shallow copy since changing either the source or the copy causes the other object to change too.

let a = {
    food: "pasta",
    restaurantName: "myPastPlace"
}

let b = a

b.food = "hamburger"

console.log(b.food) //hamburger
console.log(a.food) //hamburger

So, the confusing part was that when I use the spread syntax to make a copy. Is this deep copy or shallow copy? Because for the first level deep, to me, the spread syntax(operator) is making a deep copy. However, the MDN doc says the spread syntax creates a shallow copy rather than a deep copy.

In JavaScript, all standard built-in object-copy operations (spread syntax, Array.prototype.concat(), Array.prototype.slice(), Array.from(), Object.assign(), and Object.create()) create shallow copies rather than deep copies.

let a = {
    food: "pasta",
    restaurantName: "myPastPlace"
}

let b = {...a}
console.log(b)

b.food = "hamburger"

console.log(b.food) //hamburger
console.log(a.food) //pasta


Solution

  • A variable can contain either a value (in case of primitive values, like 1), or a reference (in case of objects, like { food: "pasta" }. Primitive types can only be copied, and since they contain no properties, the shallow/deep distinction does not exist.

    If you considered references themselves as primitive values, b = a is a copy of a reference. But since JavaScript does not give you direct access to references (like C does, where the equivalent concept is a pointer), refering to copying a reference as "copy" is misleading and confusing. In context of JavaScript, "copy" is a copy of a value, and a reference is not considered a value.

    For non-primitive values, there are three different scenarios:

    let a = {
        food: "pasta",
        contents: {
            flour: 1,
            water: 1
        }
    }
    let b = a;
    a.taste = "good";
    a.contents.flour = 2;
    console.log(b);

    let a = {
        food: "pasta",
        contents: {
            flour: 1,
            water: 1
        }
    }
    let b = {...a};
    a.taste = "good";
    a.contents.flour = 2;
    console.log(b);

    let a = {
        food: "pasta",
        contents: {
            flour: 1,
            water: 1
        }
    }
    let b = structuredClone(a);
    a.taste = "good";
    a.contents.flour = 2;
    console.log(b);

    Note that at this time structuredClone is still pretty new; if any users are using older browsers, you might need to use a polyfill.