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

How to hide the border/separation line between cells in a table

Only show the top border <table frame=above>...

Detailed steps for installing nodejs environment and path configuration in Linux

There are two ways to install nodejs in linux. On...

SQL-based query statements

Table of contents 1. Basic SELECT statement 1. Qu...

JavaScript type detection method example tutorial

Preface JavaScript is one of the widely used lang...

Docker renames the image name and TAG operation

When using docker images, images with both REPOSI...

Vue3 (III) Website Homepage Layout Development

Table of contents 1. Introduction 2. Actual Cases...

CSS realizes process navigation effect (three methods)

CSS realizes the process navigation effect. The s...

Steps for Vue3 to use mitt for component communication

Table of contents 1. Installation 2. Import into ...

How to choose the right MySQL datetime type to store your time

When building a database and writing a program, i...

Hbase installation and configuration tutorial under Linux

Table of contents Hbase installation and configur...

Vue's guide to pitfalls using throttling functions

Preface In a common business scenario, we need to...

How to install MySQL database on Ubuntu

Ubuntu is a free and open source desktop PC opera...

Use iptables and firewalld tools to manage Linux firewall connection rules

Firewall A firewall is a set of rules. When a pac...