In today’s front-end interviews, deep copies appear very frequently. Among the general questions, you may first be asked, what is deep copy, and what are the ways to implement deep copy? You may answer a few points, such as through JSON.strinfy and JSON.parse provided by the JSON object, because this This implementation method is extremely simple, just one line of code, I feel happy, I don’t panic at all if you ask me to write it by hand. So, if the interviewer asks backhand, are there any problems in implementing deep copy through the method provided by JSON? Can you give a satisfactory answer?
What are deep copy and shallow copy
For students who don’t understand deep copy, we first introduce the concepts of deep copy and shallow copy in JavaScript
, and then discuss the implementation method and the problems therein.
Public account: Code program life, personal website: https://creatorblog.cn
In JavaScript
, we often need to copy objects or arrays in order to operate without affecting the original data. At this time, we need to distinguish between the concepts of deep copy and shallow copy.
- Shallow copy: Only the first-level attributes of an object or array are copied. If the value of the attribute is a reference type, then the reference address is copied, not the real value. In this way, modifying the copied object or array may affect the original object or array.
- Deep copy: Completely copy all hierarchical attributes of an object or array. If the value of the attribute is a reference type, then copy its internal attributes recursively until all values are of basic types. In this way, modifying the copied object or array will not affect the original object or array.
For example, suppose we have an object obj
with the following structure:
let obj = {<!-- --> name: "Tom", age: 18, hobbies: ["basketball", "football"], friend: {<!-- --> name: "Jerry", age: 17 } };
If we use a shallow copy method, such as Object.assign
or the spread operator, to copy obj
and get a new object clone
, then The structure of clone
is as follows:
let clone = Object.assign({<!-- -->}, obj); // or let clone = {...obj};
The name
and age
properties of clone
are basic types, so the real values are copied, while the hobbies
and hobbies
or friend
attributes of clone
, it will affect the corresponding attributes of obj
, such as :
clone.hobbies.push("tennis"); // Modify clone's hobbies attribute console.log(obj.hobbies); // ["basketball", "football", "tennis"], obj's hobbies attribute has also been modified
If we use the deep copy method, such as JSON.parse(JSON.stringify(obj))
, to copy obj
and get a new object clone
code>, then the structure of clone
is as follows:
let clone = JSON.parse(JSON.stringify(obj));
All properties of clone
are basic types, or newly created reference types, and have no relationship with the original object. In this way, if we modify any attributes of clone
, it will not affect the corresponding attributes of obj
, for example:
clone.friend.name = "Bob"; // Modify the friend attribute of clone console.log(obj.friend.name); // "Jerry", obj's friend attribute has not been modified
Why use JSON method to implement deep copy
Using JSON.parse(JSON.stringify(obj))
to implement deep copy is a very simple and effective method.
Its principle is to use JSON.stringify
to serialize an object or array into a JSON
string, and then use JSON.parse
to parse the string For a new object or array, thus implementing deep copy.
The advantages of this approach are:
- The code is concise and can be done in one line
- There is no need to consider the hierarchical structure of objects or arrays, and nested situations can be automatically handled
- No need to consider the property names of objects or arrays, all properties can be copied automatically
Problems in implementing deep copy using JSON method
Although it is convenient to use JSON.parse(JSON.stringify(obj))
to implement deep copy, it also has many limitations and problems that we need to pay attention to. These issues mainly include:
- Cannot handle circular references: If there is a circular reference in an object or array, that is, the value of an attribute is the object or array itself, or an ancestor attribute of the object or array, then
JSON.stringify
will report an error and cannot be serialized. for example:
let obj = {<!-- --> name: "Tom", age: 18, hobbies: ["basketball", "football"], friend: {<!-- --> name: "Jerry", age: 17 } }; obj.self = obj; // The self attribute of obj points to obj itself, forming a circular reference let clone = JSON.parse(JSON.stringify(obj)); // TypeError: Converting circular structure to JSON
- Cannot handle undefined, Symbol and other types of values: If there are
undefined
,Symbol
and other types of values in the object or array, thenJSON .stringify
will lose these values and cannot be serialized. for example:
let obj = {<!-- --> name: "Tom", age: undefined, // The age property of obj is undefined hobbies: ["basketball", "football"], friend: {<!-- --> name: "Jerry", age: 17 }, [Symbol("id")]: 123 // Symbol attribute of obj }; let clone = JSON.parse(JSON.stringify(obj)); console.log(clone); // {name: "Tom", hobbies: ["basketball", "football"], friend: {name: "Jerry", age: 17}} , the age attribute and Symbol attribute of clone are lost
- Cannot process values of types such as Date and regular expressions: If there are values of types such as
Date
and regular expressions in the object or array, thenJSON.parse( JSON.stringify(obj))
will be distorted and cannot be restored to the original type. for example:
let obj = {<!-- --> name: "Tom", age: 18, hobbies: ["basketball", "football"], friend: {<!-- --> name: "Jerry", age: 17 }, birthday: new Date("2000-01-01"), // obj's birthday attribute is of Date type pattern: /\w + / // The pattern attribute of obj is a regular expression type }; let clone = JSON.parse(JSON.stringify(obj)); console.log(clone.birthday); // "2000-01-01T00:00:00.000Z", clone's birthday attribute becomes a string console.log(clone.pattern); // {}, the pattern attribute of clone becomes an empty object
- Cannot handle objects generated by constructors: If the object is generated by a constructor, then
JSON.parse(JSON.stringify(obj))
will discard the object’sconstructor
, cannot be restored to the original type. for example:
function Person(name, age) {<!-- --> this.name = name; this.age = age; } let obj = new Person("Tom", 18); // obj is generated by the Person constructor let clone = JSON.parse(JSON.stringify(obj)); console.log(clone.constructor); // [Function: Object], clone's constructor becomes Object console.log(clone instanceof Person); // false, clone is not an instance of Person
How to solve the problems of deep copying in JSON method
To solve the problems of deep copying using the JSON
method, we can adopt the following solutions:
- Use recursive method: Use recursion to traverse each attribute of the object or array, determine the type of the attribute, if it is a basic type, copy it directly, if it is a reference type, create a new object or array, and continue the recursion Copy, this method can handle circular reference situations, but you need to pay attention to the risk of stack overflow.
- Use third-party library method: Use some mature third-party libraries, such as
lodash
,jQuery
, etc., which provide some deep copy functions. Can handle various types of values, but there are some performance or compatibility issues. - Use special processing methods: For some special types, such as
Date
, regular expressions, constructors, etc., we can use some special processing methods to ensure deep copying Correctness. For example, for theDate
type, we can usenew Date(obj.getTime())
to copy a newDate
object. For regular expressions For formula types, we can usenew RegExp(obj.source, obj.flags)
to copy a new regular expression object. For constructor types, we can usenew obj.constructor( )
to copy a new constructor object.
Summary
Using JSON.parse(JSON.stringify(obj))
to implement deep copy is a simple and effective method, but it also has many limitations and problems that we need to pay attention to. These issues mainly include:
- Cannot handle circular reference situations
- Cannot handle values of types such as
undefined
andSymbol
- Cannot handle values of
Date
, regular expression and other types - Cannot handle objects generated by constructors
In order to solve these problems, we can adopt the following solutions:
- Use recursive method
- Use third-party library methods
- Use special handling methods