Personal blog posts: Daily Web front end questions (2021.05)

Shallow copy

If the property of the shallow-copied object is of reference type, it still points to the property of the same name of the source object. If you modify the properties under the reference type of the shallow-copy object, the source object is also modified. The implementation schemes are as follows:

  • {... obj}
  • Object.assign({}, obj)
  • for... inIterate over the object to get the key and place it in a new empty object.

Deep copy

If the properties under the reference type attribute are modified, the source object does not change. A real parting of the ways. Implementation of serialization and deserialization, and recursive implementation.

Serialize and deserialize

JSON. Parse (JSON. Stringify ()). The disadvantage is that:

  • Copying special objects, such as Date and RegExp, causes problems
  • Cannot handle looped objects, an error is reported
  • If two properties point to the same object, the two namesake properties of the new object point to their respective properties

Manual implementation

The idea is to iterate over attributes, recursively for attributes of reference type, until all attributes are not reference types.

Problems to be solved:

  • A circular reference is an attribute under an object that points to that object. If you recurse without special processing, an infinite loop will occur. To deal with the problem of ring, hash table is usually used to solve the problem: put the traversed object into the hash table (key is the source object, value is the new object), check whether the hash table has this object before copying, if there is a ring, directly take the corresponding value as the new object.
  • Same reference, that is, multiple properties pointing to the same object. A circular reference is a special case of the same reference. The solution is to handle circular references.
  • Special objects, ordinary objects, arrays these two most basic needless to say, JS built-in objects such as Date, RegExp should be targeted to do one by one processing. If user-defined objects need to be considered, they should also be dealt with in a targeted manner, such as adding prototype chain and implementing clone method in a targeted manner.

The implementation code is as follows (a simple unit test for each case) :

/** * Object deep copy * currently only supports normal objects and arrays ** handles the problem of circular references and identical references **@param {any} Obj The object to be copied *@returns A new object * /
const cloneDeep = (obj) = > {
  const map = new Map(a)// oldObj -> newObj

  function _cloneDeep(obj) {
    if (obj === null || typeofobj ! = ='object') return obj // Non-object value
    if (map.has(obj)) { // Check for the same reference
      return map.get(obj)
    }

    const retObj = Array.isArray(obj) ? [] : {} // Determine whether the carrier of the new object is a normal object or an array.
    // This line must be placed here, not in front of return. This is to handle "circular reference" cases
    // The loop object must already be in the map when it is found, otherwise it will loop forever.
    map.set(obj, retObj)
    for (const key in obj) { // Iterate over attributes
      retObj[key] = _cloneDeep(obj[key])
    }
    return retObj
  }

  return _cloneDeep(obj)
}
Copy the code

In practice, you don’t need an overly sophisticated deep copy; serialization and deserialization are generally sufficient. If the situation is a bit complicated, consider using loadsh.clonedeep