JavaScript structuredClone modern deep copy

There are many ways to implement deep copy in JavaScript, each with its own advantages and disadvantages. Today we introduce an implementation of deep copy provided by native JavaScript structuredClone.

Here are some common methods, along with their code examples and advantages and disadvantages:

1. UseJSON.parse(JSON.stringify(obj))

Code example:

function  deepClone ( obj ) {
     return  JSON . parse ( JSON . stringify (obj));
}

Advantages: Simple and easy to implement, effective for most object types.

Disadvantages: The prototype chain cannot be copied, and problems may occur for objects containing circular references. For example, the following code:

const calendarEvent = {
   date : new  Date ()
}

const problematicCopy = JSON . parse ( JSON . stringify (calendarEvent))

The final date obtained is not a Data object, but a string.

{ 
    "date" :  "2024-03-02T03:43:35.890Z" 
}

This is because JSON.stringifyit can only handle basic objects and arrays. Any other types are not handled as expected. For example, convert date to string. Set/Map just converts to {}.

const kitchenSink = {
   set : new  Set ([ 1 , 3 , 3 ]),
   map : new  Map ([[ 1 , 2 ]]),
   regex : /foo/ ,
   deep : { array : [ new  File (someBlobData, ' file.txt' ) ] },
   error : new  Error ( 'Hello!' )
}

const veryProblematicCopy = JSON . parse ( JSON . stringify (kitchenSink))

Finally, the following data is obtained:

{ 
  "set" :  { } , 
  "map" :  { } , 
  "regex" :  { } , 
  "deep" :  { 
    "array" :  [ 
      { } 
    ] 
  } , 
  "error" :  { } , 
}

2. Use recursion

Code example:

function  deepClone ( obj ) {
     if (obj === null || typeof obj !== 'object' ) {
         return obj;
    }
    let clone = obj. constructor ();
     for ( let attr in obj) {
         if (obj. hasOwnProperty (attr)) {
            clone[attr] = this . deepClone (obj[attr]);
        }
    }
    return clone;
}

Advantages: Valid for any type of object, including circular references.

Disadvantages: May consume a lot of memory for large objects and may cause stack overflow.

3. Third-party libraries, such as lodash’s _.cloneDeepmethod

Code example:

const _ = require ( 'lodash' );
 function  deepClone ( obj ) {
     return _. cloneDeep (obj);
}

Advantages: Supports more types of objects and libraries, for example, supports Proxy objects.

Disadvantages: It will introduce dependencies and increase the size of the project.

Because this function will lead to the introduction of 17.4kb dependencies, it will be higher if only lodash is introduced.

4. Modern deep copy structuredClone

In modern browsers, you can use structuredClonethe method to implement deep copy, which is a more efficient and safer way of deep copying.

Here is a sample code that demonstrates how to use structuredCloneto make a deep copy:

const kitchenSink = {
   set : new  Set ([ 1 , 3 , 3 ]),
   map : new  Map ([[ 1 , 2 ]]),
   regex : /foo/ ,
   deep : { array : [ new  File (someBlobData, ' file.txt' ) ] },
   error : new  Error ( 'Hello!' )
}
kitchenSink.circular = kitchenSink

const clonedSink = structuredClone (kitchenSink)

structuredClonecan do:

  • Copy infinitely nested objects and arrays
  • copy circular reference
  • Copy various JavaScript types, such as , DateSetMapErrorRegExpArrayBufferBlobFileetc.ImageData

What cannot be copied:

  • function
  • DOM node
  • attribute description, setterandgetter
  • object prototype chain

Full list supported:

ArrayArrayBufferBooleanDataViewDateErrortypes (the types specifically listed below), MapObject, but only ordinary objects, primitive types, except symbol(aka numberstringnullundefinedbooleanBigInt), RegExpSet,TypedArray

Error type:

ErrorEvalErrorRangeErrorReferenceErrorSyntaxErrorTypeError,URIError

Web/API type:

AudioDataBlobCryptoKeyDOMExceptionDOMMatrixDOMMatrixReadOnlyDOMPointDomQuadDomRectFileFileListFileSystemDirectoryHandleFileSystemFileHandleFileSystemHandleImageBitmapImageDataRTCCertificate,VideoFrame

Thankfully it is structuredClonesupported in all major browsers, as well as Node.js and Deno.

at last

structuredCloneWe can now finally deep copy objects directly using the capabilities of native JavaScript . Each method has its pros and cons, and how you use it depends on your needs and the type of audience you’re targeting.

refer to

  • Deep Cloning Objects in JavaScript, the Modern Way(www.builder.io/blog/structured-clone)
  • mozilla structuredClone (developer.mozilla.org/en-US/docs/Web/API/structuredClone)