preface

When I was in an interview with a company, the interviewer asked me a question :” How do I deep copy an object?” At that time MY heart some secretly pleased, so simple problem still use to think? Parse (json.stringify (obj)); parse(json.stringify (obj)) In plus recursive completion “. The interviewer nodded with satisfaction. At that time, I did not care much about this problem, until some time ago when I thought about this problem, I found that the two methods mentioned above are buggy.

Ask questions

So what are these bugs?

  • Special object copy

Let’s first imagine an object that has the following members, regardless of the ordinary type:





Const obj = {arr: [111, 222], obj: {key: 'objects'}, a: () = > {the console. The log (' function')}, date: new date (), reg: / regular/ig}Copy the code

Then we will copy each one in the above two ways

JSON method


JSON.parse(JSON.stringify(obj))
Copy the code







As you can see, the regular objects and arrays in obj can be copied, whereas the date object becomes a string, and the function disappears. The re is an empty object.

Let’s look at for… In plus recursion

recursive

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 }Copy the code







conclusion

From the above tests, we can see that neither method can copy the function, date, reg type object;

  • ring
What is a ring?

A loop is a circular reference to an object that causes itself to be a closed loop, such as the following object:



var a = {}

a.a = a
Copy the code






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



The solution

  • ring

A WeakMap structure can be used to store the object that has been copied. Every time when copying, we will first check whether the object has been copied to WeakMap. If it has been copied, we will take out the object and return it, and 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 the code







  • A copy of a special object

This problem is difficult to solve, because there are too many types of objects to be treated specially, so I refer to the structured copy on MDN, and then combine the solution to solve the ring:


// Only date, reg, 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 the code







The full version can be viewed in lodash deep copy

  • Function copy

But the structured copy on MDN still does not solve the function copy



So far, I’ve only thought of copying functions using eval, but this only works with arrow functions, and fun(){} will fail

Copy a function to add a function type



Wrong type



Afterword.

Deep copy of JavaScript is not just about these potholes, but also about how to copy properties on the prototype chain. How do I copy non-enumerable properties? How to copy Error objects and so on pit, here is not a repeat.

However, it is recommended to use THE JSON method in the daily process. This method covers most of the business requirements, so there is no need to complicate simple things, but in the interview, if the interviewer is trying to find a solution to this question, it will definitely show him.












The Denver nuggets