Some pitfalls of JavaScript deep copy

Some pitfalls of JavaScript deep copy

Preface

When I went to an interview at a company before, the interviewer asked me a question, saying: "How can I deeply copy an object?" At that time, I felt secretly delighted. Is it necessary to think about such a simple question? So I blurted out: "There are two commonly used methods. The first is to use JSON.parse(JSON.stringify(obj)), and the second is to use for...in plus recursion." After listening to this, the interviewer nodded and was quite satisfied.
I didn't care too much about this problem at the time, until I thought about it again some time ago and found that both methods mentioned above had bugs.

Ask a question

So what is the Bug mentioned above?

Special object copy

First, let's imagine an object that has the following members, without considering common types:

const obj = {
    arr: [111, 222],
    obj: {key: 'object'},
    a: () => {console.log('function')},
    date: new Date(),
    reg: /regular/ig
}

Then we copy it once using the above two methods respectively

JSON Method

JSON.parse(JSON.stringify(obj))

Output:

It can be seen that both the ordinary objects and arrays in obj can be copied, but the date object becomes a string, the function disappears directly, and the regular expression becomes an empty object.
Let's take a look at the for...in method with recursion

recursion

function isObj(obj) {
    return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}
function deepCopy(obj) {
    let tempObj = Array.isArray(obj) ? [] : {}
    for(let key in obj) {
        tempObj[key] = isObj(obj[key]) ? deepCopy(obj[key]) : obj[key]
    }
    return tempObj
}

result:

in conclusion

From the above test, we can see that these two methods cannot copy objects of function, date, and reg types;

  • ring

What is a ring?

A loop is a circular reference between objects, which results in a closed loop. For example, the following object:

var a = {}

aa = a

Using the above two methods to copy will directly report an error

Solution

  • ring

You can use a WeakMap structure to store objects that have been copied. Each time you copy an object, query the WeakMap to see if the object has been copied. If it has been copied, take out the object and return it. Transform the deepCopy function into the following

function deepCopy(obj, hash = new WeakMap()) {
    if (hash.has(obj)) return hash.get(obj)
    let cloneObj = Array.isArray(obj) ? [] : {}
    hash.set(obj, cloneObj)
    for (let key in obj) {
        cloneObj[key] = isObj(obj[key]) ? deepCopy(obj[key], hash) : obj[key];
    }
    return cloneObj
}

Copy ring result:

Copying of special objects

The solution to this problem is rather complicated, because there are too many types of objects that need to be treated specially, so I referred to the structured copy on MDN, and then combined it with the solution to the ring:

// Only solve date and reg types, others can be added by yourself function deepCopy(obj, hash = new WeakMap()) {
    let cloneObj
    let Constructor = obj.constructor
    switch(Constructor){
        case RegExp:
            cloneObj = new Constructor(obj)
            break
        Case Date:
            cloneObj = new Constructor(obj.getTime())
            break
        default:
            if (hash.has(obj)) return hash.get(obj)
            cloneObj = new Constructor()
            hash.set(obj, cloneObj)
    }
    for (let key in obj) {
        cloneObj[key] = isObj(obj[key]) ? deepCopy(obj[key], hash) : obj[key];
    }
    return cloneObj
}

Copy result:

For the full version, see lodash deep copy

  • Copy of function

However, the structured copy on MDN still does not solve the problem of function copying.

So far, I have only thought of using the eval method to copy the function, but this method only works for arrow functions. If it is in the form of fun(){}, it will fail.

Copy function to add function type

Copy result

Error type

postscript

JavaScript's deep copy has more problems than the ones mentioned above. Another problem is how to copy the properties on the prototype chain? How to copy non-enumerable properties? How to copy Error objects, etc., I will not go into details here.

However, it is still recommended to use the JSON method in daily life. This method has covered most of the business needs, so there is no need to complicate simple things. However, if you encounter an interviewer who is nitpicking during the interview, your answer to this question will definitely make him look good.

This concludes this article about some pitfalls of JavaScript deep copy. For more relevant JavaScript deep copy content, please search 123WORDPRESS.COM’s previous articles or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • A brief discussion on JavaScript shallow copy and deep copy
  • Detailed description of shallow copy and deep copy in js
  • Detailed explanation of JS variable storage deep copy and shallow copy
  • JS object copying (deep copy and shallow copy)
  • Detailed explanation of JS deep copy and shallow copy
  • Let you understand the deep copy of js

<<:  Implementation of crawler Scrapy image created by dockerfile based on alpine

>>:  Implementation of tomcat image created with dockerfile based on alpine

Recommend

Tutorial on installing mysql5.7.23 on Ubuntu 18.04

This article shares with you the specific method ...

Detailed description of common events and methods of html text

Event Description onactivate: Fired when the objec...

Method to detect whether ip and port are connectable

Windows cmd telnet format: telnet ip port case: t...

Detailed steps to install xml extension in php under linux

Installing XML extension in PHP Linux 1. Enter th...

Tomcat class loader implementation method and example code

Tomcat defines multiple ClassLoaders internally s...

Complete steps for mounting a new data disk in CentOS7

Preface I just bought a new VPS. The data disk of...

Ideas and codes for implementing waterfall flow layout in uniapp applet

1. Introduction Is it considered rehashing old st...

How to expand the disk size of a virtual machine

After Vmvare sets the disk size of the virtual ma...

How to use the yum command

1. Introduction to yum Yum (full name Yellow dogU...

MySQL implements an example method of logging in without a password

Specific method: Step 1: Stop the mysql service /...

How to set up jar application startup on CentOS7

Pitfalls encountered during project deployment Wh...

Solution to JS out-of-precision number problem

The most understandable explanation of the accura...