preface

Since the birth of JavasSript, it has been impossible to simply make a deep copy of a reference data type. The HTML specification now defines the structuredClone API, which is a browser-hosted built-in function for deep copy (Node and Deno are also implemented). Take this opportunity to review deep copy and shallow copy in JS.

Shallow copy vs deep copy

Shallow copy

In JavaScript, almost all copy methods are shallow copies, which means that if the underlying data of the original object changes, the copied object will also change because they keep the same references, a feature that often causes bugs.

The shallow copy method is commonly used

  1. .Expansion operator
const original = {a: {b:1}}
constshallowCloned = {... original}Copy the code

2.Object.assign()

const original = {a: {b:1}}
const shallowCloned = Object.assign({},original)
Copy the code

Deep copy

Compared with the shallow copy, the deep copy copies all the data at all levels of the original scene. The deep-level data of the original object is changed without changing the value of the copied object.

The deep-copy method is commonly used

  1. JSON.parse(JSON.stringify(target))

Defects: 1. Ignore functions; 2. Ignore the prototype chain; 3. The attribute whose value is undefined is ignored. 4. If the data level is very high, the stack overflow occurs.

JSON.stringify({a:undefined.b:() = >{}})/ / 🌟 '{}'
JSON.stringify(undefined) //undefined
Copy the code
const original = {a: {b:1}}
const deepCloned = JSON.parse(JSON.stringify(original))
Copy the code
  1. Handwritten deep copy – recursive

Defects: 1. Circular reference to be resolved…

function getType(target) {
  return Object.prototype.toString.call(target).slice(8, -1)}function deepClone(target) {
  // Handle primitive values null, undefined, number, string, symbol, bigInt, Boolean
  if (typeoftarget ! = ='object' || target === null) {
    return target
  }
  / / array processing
  if (Array.isArray(target)) {
    return target.map((e) = > deepClone(e))
  }
  / / processing function
  if (getType(target) === 'Function') {
    return eval(` (${target.toString()}) `).bind(this) // function declares that it needs to be wrapped with "(", ")"
  }
  // Copy the date
  if(getType(target) === 'Date') {
    return new Date(target.valueOf()) 
 }
  // Copy the re
  if(getType(target) === 'RegExp') {
    return new RegExp(target)
 }
  / / the map processing
  if (getType(target) === 'Map') {
    let map = new Map()
    target.forEach((v, k) = > {
      map.set(k, deepClone(v))
    })
    return map
  }
  / / handle the set
  if (getType(target) === 'Set') {
    let set = new Set(a)for (let val of target.values()) {
      set.add(deepClone(val))
    }
    return set
  }
  / / object
  if (getType(target) === 'Object') {
    let cloneTarget = {}
    for (let key in target) {
      cloneTarget[key] = deepClone(target[key])
    }
    return cloneTarget
  }
  return target
}
Copy the code

StructuredClone API is introduced

StructuredClone is an implementation of the structured copy algorithm, capable of implementing deep copies of almost all data types. The latest versions of the browser have native support.

grammar

structuredClone(value)
structuredClone(value, { transfer })
Copy the code

parameter

  • value

    The object you want to copy.

  • Transfer (optional)

    Transferable object: an array in which values are moved to the new object rather than copied to the new object.

The return value

Returns the object after a deep copy of the passed object.

abnormal

If the value passed is an object that cannot be deeply copied, a DATA_CLONE_ERR error is thrown.

limit

  • Cloning is not allowedError,FunctionandDOMObject, if the object containsDATA_CLONE_ERRThe exception.
  • Don’t keepRegExp The object’slastIndexField.
  • Do not retain attribute descriptors,settersAs well asgetters(and other metadata-like features). For example, if an object is marked with an attribute descriptor asread-only, it will be copied toread-write.
  • Do not retain the original chain.

The sample

  1. Copying ordinary objects
const original = {a: {b:1}}
const cloned = structuredClone(original)
console.log(cloned) // {a:{b:1}}
console.log(origin.a===cloned.a) // false
Copy the code
  1. Copy function
const original = {a:() = >{}}
const cloned = structuredClone(original)
// Uncaught DOMException: Failed to execute 'structuredClone' on 'Window': ()=>{} could not be cloned.
Copy the code
  1. Copy the tape prototype object

The prototype chain of the original object is gone

compatibility

The structuredClone API is supported by the latest versions of major browsers

Firefox >= 94; Chrome(Edge) >=98; Safari > = 15.4;

Core-js # structuredClone can be introduced, compatible with older versions.

reference

  1. “structuredClone” | Can I use… Support tables for HTML5, CSS3, etc
  2. structuredClone() – Web APIs | MDN (mozilla.org)