3

Shallow Copy vs Deep Copy in JavaScript | by Jason Kuffler | Jan, 2022 | Bits an...

 2 years ago
source link: https://blog.bitsrc.io/shallow-copy-and-deep-copy-in-javascript-d0ca570cd4cf
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Shallow Copy vs Deep Copy in JavaScript

It’s important to understand what’s happening with Shallow and Deep copies in JavaScript. To understand this, we will take a look at memory allocation and its behavior in JavaScript.

Our goal today is to answer this: why should we be concerned with these concepts and their differences?

If you’re just discovering the shallow copy or looking for clarification then I recommend bookmarking 3 Ways to Shallow Clone Objects in JavaScript . The author’s bonuses are a lot of fun! I like having a JavaScript replit ready to run side-by-side when reading technical docs. It is imperative to have the node environment of your choice running so you can practice while you get a feel for the water.

It’s also wise to track your files somewhere using something like GitHub or Bit. That way you can cloud save your more commonly used, abstracted, and refactored code blocks. Then take out your working bits from replit notes/exercises and create the refactored features inside of a robust space that GitHub or Bit can track. Once you’ve got a feature or solution working, clean, and scalable; then, it can likely be used elsewhere in your work — so why not make it a green square? My goal with this generalized, mutable procedure is to satisfy best practices for modular building with reusable code in addition to understanding what’s operating under the hood when implementing solutions as much as possible.

Further reading which helped me create my own examples and explanations (below) can be found at MDN’s JS Memory Management.

I’ve shared the sources which piqued my interests a bit. Now I want to show you how I like to play with code after I’ve digested some material. The included further reading will breeze by if you’re working along with this article.

Below is a basic copy where a memory space is created for new copies — both immutable (when copied) by virtue of their primitive values/data types. In other words: they are assigned/passed by value.

//Primitive value copylet  x = 400
let y = x x = "This string"console.log(y) //400
console.log(x) //This string

When y is assigned it’s value it copied x’s value (as it was during run time — before x is reassigned). The important takeaway here is that you can quickly copy a primitive data type’s exact value in a separate memory space by creating and assigning another variable to the variable being copied. Take note of how it is instantiated — const will not allow later changes.

Visual progression of primitive value copy
Visual progression of primitive value copy
Visual Progression of Memory Allocation and Primitive value assignment
//We can go a little further with the above block and re-assign x to the original value; however, we know they are separate memory spaces with same valuesx = y
console.log(x) // 400
console.log(y) // 400

The Main Focus

There are more complex data types and structures we have to work with: reference values — such as objects and arrays (which are a specific type of JS object). An object has accessible properties aka “keys”. These keys have their own values that are available to be modified. JS objects (as non-primitive data types) differ because they have reference values and those values are mutable. This means they share a memory address when shallow copied.

Note: the example below is a shallow object-literal being shallow copied — meaning their values will be the same because they are both references to the same memory space. In JS a “shallow object” is a non-nested, non-primitive JS datatype.

//Shallow Clone of a Shallow Object
let shallowObj = {
key1: 1,
key2: 2,
}
let newObj = shallowObj // a simple reassignment creates shared
// memory for newObj
shallowObj.key1 = 5console.log(shallowObj) // {key1: 5, key2: 2}
console.log(newObj) // {key1: 5, key2: 2}

Notice that re-assigning value at key1 of shallowObj changes newObj’s key1 value as well. They share a space in memory and have separate variable names! Where shallowObj.key1 is changed we could instead update newObj.key1 and we get the same result.

This is useful in certain situations where we want to represent a bunch of the same properties and values of a particular object; however, later on in run time those shallow cloned objects could require different variable names to work with your app’s goals — passed as different props to other components for different purposes.

I’d love to hear more about how this concept has real-world application. It bears mentioning that understanding this helps later.

Before going on I want to emphasize that a deep copy differs from the shallow copy in this way: the deep copy is allocating a new memory address for some or all (depending on complexity of data) the values from the original variable. A deep copy goes further and passses elements’ values.

Deep Copies of a Shallow-Object

 //Using Object.assign()let myRadio = { podcasts: 19, 
albums: 378,
playlists: 44
}
let deepCopyMyRadio = Object.assign( {}, myRadio )deepCopyMyRadio.playlists = 62 // only changes deepCopyMyRadioconsole.log(deepCopyMyRadio) // => { podcasts: 19,
albums: 378,
playlists: 62
}
console.log(myRadio) // => { podcasts: 19,
albums: 378,
playlists: 44
}

ES6 introduced a little syntactic sugar with the Spread Operator. The spread operator is represented by the three consecutive dots “” and gets used in a few different parts of code. In general the spread operator is making a copy for each (top-level) property of a given object and then spreading them to a new object.

It’s up to us to decide when we would like a shallow or deep copy of a particular nested object. For now we’re showing a deep copy of a shallow object so either the Object.assign() method or the example below will do.

//Deep Copy with spread operator let myRadio = { podcasts: 9, 
albums: 38,
playlists: 4
}
let copyMyRadio = { ...myRadio }myRadio.albums = 88 //again only changes myRadioconsole.log myRadio //=> { podcasts: 9, albums: 88, playlists: 4 }
console.log copyMyRadio //=> {podcasts: 9,albums: 38, playlists: 4}

The trouble is that a spread operator is a limited solution as scale and complexity increases within a project. Spread operator can handle a deep-copy of a shallow object (non-nested — above). With lightweight code blocks the spread operator performs great.

In this case someOtherVar would be assigned to someVar. When one variable’s value is changed the other will be as well.
In this case someOtherVar would be assigned to someVar. When one variable’s value is changed the other will be as well.
In this case the copy is assigned as such: someOtherVar = someVar

Below we see the spread operator won’t handle the complexity of a nested object as we may expect.

For nested objects the spread operator will provide a deep copy to the first instance of the values but leaves all the nested data as shallow copies sharing a space in memory with original. Take note of this behavior!

population.total shares a memory spot in both city and shallowCity. spread operator grabs top lair data and adds it to separate memory space; hence, the name property is actually changed for shallowCity.

Reminder: we, the wizards, decide which is best (shallow or deep copy) for the problem we’re solving/memory usage of a program.

Do we need to pass references alone or their values as well? Is it necessary to create a new piece of memory for a variable or can it share the values of the original object?

A specific use for deep-copy comes to mind: creating a deeply nested object to serve as a template. A template will need to be copied into a separate memory space along with any nested default values.

This way we can make programmatic changes to copies and set ourselves up for dynamic use of an object throughout the project.

Finally we deep copy a deep object using a highly popular method with ready-made tools in ES6. There are others worth understanding found here.

The Solution for Deep Copy of Deep Data: JSON.parse(JSON.stringify())

Here we have an example of my family’s game library. Once upon a time I was able to play a bunch of big-boy video games . . .

Line 78 is where we use ES6 to make a new spot in memory for adultGames whose new values are modified lines 80–82

The drawback for copying objects/arrays in vanilla JavaScript is with nested values. As you can see: drilling in, while easy enough, can be tedious.

I really liked these other cool uses for the ES6 spread operator with these executions! If you’re wondering what I meant earlier about spread operator being used in different parts of code you should definitely check it out.

This article is a very thorough breakdown of possible uses for copying arrays and objects with other built-in ES6 methods!

All info presented here is to the best of my understanding. Any corrections or feedback are welcome in the comments.

Use any component in all your projects

Up until now, you used to build features hidden inside larger projects.

But what if you were to develop independent features first, and then easily compose and manage them in many applications? Your development will become faster, more consistent, and more scalable every day. Create a component once, and truly use it anywhere to build anything.

OSS Tools like Bit offer a powerful developer experience for building independent components and composing them to applications. You can start small with a nice project, some shared components, or even trying out Micro Frontends. Give it a try →

0*jtptiXcO0XphNxQP?q=20
shallow-copy-and-deep-copy-in-javascript-d0ca570cd4cf
An independent product component that can be used anywhere.

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK