<span class="latin" style="width:19px;height:19px;">H</span><span class="latin" style="width:19px;height:19px;">T</span><span class="latin" style="width:19px;height:19px;">M</span><span class="latin" style="width:19px;height:19px;">L</span><span class="latin" style="display:block;width:19px;height:19px;"> </span><span class="latin" style="width:19px;height:19px;">S</span><span class="latin" style="width:19px;height:19px;">t</span><span class="latin" style="width:19px;height:19px;">a</span><span class="latin" style="width:19px;height:19px;">n</span><span class="latin" style="width:19px;height:19px;">d</span><span class="latin" style="width:19px;height:19px;">a</span><span class="latin" style="width:19px;height:19px;">r</span><span class="latin" style="width:19px;height:19px;">d</span>  
(一)2.7 Safe passing of structured data
(一)2.7.1 Serializable objects
(二)2.7.2 Transferable objects
(三)2.7.3 StructuredSerializeInternal ( value, forStorage [ ,  memory ] )
(四)2.7.4 StructuredSerialize ( value )
(五)2.7.5 StructuredSerializeForStorage ( value )
(六)2.7.6 StructuredDeserialize ( serialized, targetRealm [ ,  memory ] )
(七)2.7.7 StructuredSerializeWithTransfer ( value, transferList  )
(八)2.7.8 StructuredDeserializeWithTransfer ( serializeWithTransferResult,  targetRealm )
(九)2.7.9 Performing serialization and  transferring from other specifications
(十)2.7.10 Structured cloning API

2.7 Safe passing of structured data


To support passing JavaScript objects,  including platform objects, across realm  boundaries, this specification defines the following infrastructure for  serializing and deserializing objects, including in some cases transferring the underlying data  instead of copying it. Collectively this serialization/deserialization process is known as  "structured cloning", although most APIs perform separate serialization and deserialization steps.  (With the notable exception being the structuredClone()  method.)

This section uses the terminology and typographic conventions from the JavaScript  specification. [JAVASCRIPT]

2.7.1 Serializable objects


/developer.mozilla.org/en-US/docs/Glossary/Serializable_object
Firefox103+SafariNoChrome77+

Opera?Edge79+

Edge (Legacy)?Internet ExplorerNo

Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
/developer.mozilla.org/en-US/docs/Glossary/Serializable_object
Firefox103+SafariNoChrome77+

Opera?Edge79+

Edge (Legacy)?Internet ExplorerNo

Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
/developer.mozilla.org/en-US/docs/Glossary/Serializable_object
Firefox103+SafariNoChrome77+

Opera?Edge79+

Edge (Legacy)?Internet ExplorerNo

Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
/developer.mozilla.org/en-US/docs/Glossary/Serializable_object
Firefox103+SafariNoChrome77+

Opera?Edge79+

Edge (Legacy)?Internet ExplorerNo

Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
/developer.mozilla.org/en-US/docs/Glossary/Serializable_object
Firefox103+SafariNoChrome77+

Opera?Edge79+

Edge (Legacy)?Internet ExplorerNo

Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
/developer.mozilla.org/en-US/docs/Glossary/Serializable_object
Firefox103+SafariNoChrome77+

Opera?Edge79+

Edge (Legacy)?Internet ExplorerNo

Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
/developer.mozilla.org/en-US/docs/Glossary/Serializable_object
Firefox103+SafariNoChrome77+

Opera?Edge79+

Edge (Legacy)?Internet ExplorerNo

Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

Serializable objects support being serialized, and later deserialized, in a way  that is independent of any given realm. This allows them to be stored on disk and  later restored, or cloned across agent and even agent cluster  boundaries.

Not all objects are serializable objects, and not all aspects of objects that are  serializable objects are necessarily preserved when they are serialized.

Platform objects can be serializable objects  if their primary interface is decorated with the [Serializable] IDL extended  attribute. Such interfaces must also define the following algorithms:


serialization steps, taking a platform object  value, a Record serialized, and a boolean  forStorage


A set of steps that serializes the data in value into fields of  serialized. The resulting data serialized into serialized must be  independent of any realm.

These steps may throw an exception if serialization is not possible.

These steps may perform a sub-serialization to serialize nested data  structures. They should not call StructuredSerialize directly, as doing so will  omit the important memory argument.

The introduction of these steps should omit mention of the forStorage argument if  it is not relevant to the algorithm.
 


deserialization steps, taking a Record serialized,  a platform object value, and a realm  targetRealm


A set of steps that deserializes the data in serialized, using it to set up  value as appropriate. value will be a newly-created instance of the  platform object type in question, with none of its internal data set up; setting  that up is the job of these steps.

These steps may throw an exception if deserialization is not possible.

These steps may perform a sub-deserialization to deserialize nested data  structures. They should not call StructuredDeserialize directly, as doing so will  omit the important targetRealm and memory arguments.
 


It is up to the definition of individual platform objects to determine what data is serialized  and deserialized by these steps. Typically the steps are very symmetric.

The [Serializable] extended attribute must take no  arguments, and must only appear on an interface. It must not appear more than once on an  interface.

For a given platform object, only the object's primary interface is  considered during the (de)serialization process. Thus, if inheritance is involved in defining the  interface, each [Serializable]-annotated interface in the  inheritance chain needs to define standalone serialization steps and  deserialization steps, including taking into account any important data that might  come from inherited interfaces.


Let's say we were defining a platform object Person, which had  associated with it two pieces of associated data:

a name value, which is a string; and
a best friend value, which is either another Person instance  or null.

We could then define Person instances to be serializable  objects by annotating the Person interface with the [Serializable] extended attribute, and defining the  following accompanying algorithms:


Their serialization steps, given value and serialized:

Set serialized.[[Name]] to value's associated name  value.
Let serializedBestFriend be the sub-serializationofvalue's associated best friend value.
Set serialized.[[BestFriend]] to serializedBestFriend.
 


Their deserialization steps, given serialized, value, and  targetRealm:

Set value's associated name value to  serialized.[[Name]].
Let deserializedBestFriend be the sub-deserializationofserialized.[[BestFriend]].
Set value's associated best friend value to  deserializedBestFriend.
 


Objects defined in the JavaScript specification are handled by the  StructuredSerialize abstract operation directly.

Originally, this specification defined the concept of  "cloneable objects", which could be cloned from one realm to another. However, to  better specify the behavior of certain more complex situations, the model was updated to make the  serialization and deserialization explicit.

2.7.2 Transferable objects


Transferable objects support being transferred across agents. Transferring is effectively recreating the object while sharing a  reference to the underlying data and then detaching the object being transferred. This is useful  to transfer ownership of expensive resources. Not all objects are transferable  objects and not all aspects of objects that are transferable objects are  necessarily preserved when transferred.

Transferring is an irreversible and non-idempotent operation. Once an object has  been transferred, it cannot be transferred, or indeed used, again.

Platform objects can be transferable objects  if their primary interface is decorated with the [Transferable] IDL extended  attribute. Such interfaces must also define the following algorithms:


transfer steps, taking a platform object  value and a Record dataHolder


A set of steps that transfers the data in value into fields of  dataHolder. The resulting data held in dataHolder must be  independent of any realm.

These steps may throw an exception if transferral is not possible.
 


transfer-receiving steps, taking a Record  dataHolder and a platform object value


A set of steps that receives the data in dataHolder, using it to set up  value as appropriate. value will be a newly-created instance of the  platform object type in question, with none of its internal data set up; setting  that up is the job of these steps.

These steps may throw an exception if it is not possible to receive the transfer.
 


It is up to the definition of individual platform objects to determine what data is transferred  by these steps. Typically the steps are very symmetric.

The [Transferable] extended attribute must take no  arguments, and must only appear on an interface. It must not appear more than once on an  interface.

For a given platform object, only the object's primary interface is  considered during the transferring process. Thus, if inheritance is involved in defining the  interface, each [Transferable]-annotated interface in the  inheritance chain needs to define standalone transfer steps and  transfer-receiving steps, including taking into account any important data that might  come from inherited interfaces.

Platform objects that are transferable  objects have a [[Detached]] internal slot. This  is used to ensure that once a platform object has been transferred, it cannot be transferred  again.

Objects defined in the JavaScript specification are handled by the  StructuredSerializeWithTransfer abstract operation directly.

2.7.3 StructuredSerializeInternal ( value, forStorage [ ,  memory ] )


The StructuredSerializeInternal abstract operation takes as input a JavaScript  value value and serializes it to a realm-independent form, represented  here as a Record. This serialized form has all the information necessary to later  deserialize into a new JavaScript value in a different realm.

This process can throw an exception, for example when trying to serialize un-serializable  objects.


Ifmemory was not supplied, let memory be an empty map.

The purpose of the memory map is to avoid serializing objects twice.  This ends up preserving cycles and the identity of duplicate objects in graphs.

Ifmemory[value] exists, then  return memory[value].
Let deep be false.
Ifvalue is undefined, null, a Boolean, a Number, a BigInt, or a String, then return { [[Type]]: "primitive", [[Value]]:  value }.
Ifvalue is a Symbol, then throw a  "DataCloneError" DOMException.
Let serialized be an uninitialized value.
Ifvalue has a [[BooleanData]] internal slot, then set serialized to  { [[Type]]: "Boolean", [[BooleanData]]: value.[[BooleanData]] }.
Otherwise, if value has a [[NumberData]] internal slot, then set  serialized to { [[Type]]: "Number", [[NumberData]]: value.[[NumberData]]  }.
Otherwise, if value has a [[BigIntData]]  internal slot, then set serialized to { [[Type]]: "BigInt", [[BigIntData]]:  value.[[BigIntData]] }.
Otherwise, if value has a [[StringData]] internal slot, then set  serialized to { [[Type]]: "String", [[StringData]]: value.[[StringData]]  }.
Otherwise, if value has a [[DateValue]] internal slot, then set  serialized to { [[Type]]: "Date", [[DateValue]]: value.[[DateValue]]  }.
Otherwise, if value has a [[RegExpMatcher]] internal slot, then set  serialized to { [[Type]]: "RegExp", [[RegExpMatcher]]:  value.[[RegExpMatcher]], [[OriginalSource]]: value.[[OriginalSource]],  [[OriginalFlags]]: value.[[OriginalFlags]] }.

Otherwise, if value has an [[ArrayBufferData]] internal slot, then:


IfIsSharedArrayBuffer(value) is true, then:

If the current settings object's cross-origin isolated  capability is false, then throw a "DataCloneError"  DOMException.

This check is only needed when serializing (and not when deserializing) as  the cross-origin  isolated capability cannot change over time and a SharedArrayBuffer  cannot leave an agent cluster.

IfforStorage is true, then throw a  "DataCloneError" DOMException.
Ifvalue has an [[ArrayBufferMaxByteLength]] internal slot, then set  serialized to { [[Type]]: "GrowableSharedArrayBuffer", [[ArrayBufferData]]:  value.[[ArrayBufferData]], [[ArrayBufferByteLengthData]]:  value.[[ArrayBufferByteLengthData]], [[ArrayBufferMaxByteLength]]:  value.[[ArrayBufferMaxByteLength]], [[AgentCluster]]: the surrounding  agent's agent cluster }.
Otherwise, set serialized to { [[Type]]: "SharedArrayBuffer",  [[ArrayBufferData]]: value.[[ArrayBufferData]], [[ArrayBufferByteLength]]:  value.[[ArrayBufferByteLength]], [[AgentCluster]]: the surrounding  agent's agent cluster }.


Otherwise:

IfIsDetachedBuffer(value) is true, then throw a  "DataCloneError" DOMException.
Let sizebevalue.[[ArrayBufferByteLength]].

Let dataCopy be ? CreateByteDataBlock(size).

This can throw a RangeError exception  upon allocation failure.

Perform CopyDataBlockBytes(dataCopy, 0,  value.[[ArrayBufferData]], 0, size).
Ifvalue has an [[ArrayBufferMaxByteLength]] internal slot, then set  serialized to { [[Type]]: "ResizableArrayBuffer", [[ArrayBufferData]]:  dataCopy, [[ArrayBufferByteLength]]: size,  [[ArrayBufferMaxByteLength]]: value.[[ArrayBufferMaxByteLength]] }.
Otherwise, set serialized to { [[Type]]: "ArrayBuffer",  [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size  }.
 


Otherwise, if value has a [[ViewedArrayBuffer]] internal slot, then:

IfIsArrayBufferViewOutOfBounds(value) is true, then throw a  "DataCloneError" DOMException.
Let buffer be the value of value's [[ViewedArrayBuffer]] internal  slot.
Let bufferSerialized be ?  StructuredSerializeInternal(buffer, forStorage,  memory).
Assert: bufferSerialized.[[Type]] is "ArrayBuffer",  "ResizableArrayBuffer", "SharedArrayBuffer", or "GrowableSharedArrayBuffer".
Ifvalue has a [[DataView]] internal slot, then set serialized to  { [[Type]]: "ArrayBufferView", [[Constructor]]: "DataView", [[ArrayBufferSerialized]]:  bufferSerialized, [[ByteLength]]: value.[[ByteLength]], [[ByteOffset]]:  value.[[ByteOffset]] }.

Otherwise:

Assert: value has a [[TypedArrayName]] internal slot.
Set serialized to { [[Type]]: "ArrayBufferView", [[Constructor]]:  value.[[TypedArrayName]], [[ArrayBufferSerialized]]: bufferSerialized,  [[ByteLength]]: value.[[ByteLength]], [[ByteOffset]]:  value.[[ByteOffset]], [[ArrayLength]]: value.[[ArrayLength]] }.
 


Otherwise, if value has a [[MapData]] internal slot, then:

Set serialized to { [[Type]]: "Map", [[MapData]]: a new empty List }.
Set deep to true.


Otherwise, if value has a [[SetData]] internal slot, then:

Set serialized to { [[Type]]: "Set", [[SetData]]: a new empty List }.
Set deep to true.


Otherwise, if value has an [[ErrorData]] internal slot and value is not  a platform object, then:

Let name be ? Get(value,  "name").
Ifname is not one of "Error", "EvalError", "RangeError", "ReferenceError",  "SyntaxError", "TypeError", or "URIError", then set name to "Error".
Let valueMessageDesc be ? value.[[GetOwnProperty]]("message").
Let message be undefined if  IsDataDescriptor(valueMessageDesc) is false, and  ? ToString(valueMessageDesc.[[Value]]) otherwise.
Set serialized to { [[Type]]: "Error", [[Name]]: name,  [[Message]]: message }.

User agents should attach a serialized representation of any interesting accompanying  data which are not yet specified, notably the stack property, to  serialized.

See the Error Stacks proposal for in-progress work on specifying  this data. [JSERRORSTACKS]
 


Otherwise, if value is an Array exotic object, then:

Let valueLenDescriptor be ?  OrdinaryGetOwnProperty(value, "length").
Let valueLenbevalueLenDescriptor.[[Value]].
Set serialized to { [[Type]]: "Array", [[Length]]: valueLen,  [[Properties]]: a new empty List }.
Set deep to true.


Otherwise, if value is a platform object that is a serializable object:

Ifvalue has a [[Detached]] internal slot whose value is true,  then throw a "DataCloneError" DOMException.
Let typeString be the identifier of the primary interfaceofvalue.
Set serialized to { [[Type]]: typeString }.
Set deep to true.

Otherwise, if value is a platform object, then throw a  "DataCloneError" DOMException.
Otherwise, if IsCallable(value) is true, then throw a  "DataCloneError" DOMException.

Otherwise, if value has any internal slot other than [[Prototype]],  [[Extensible]], or [[PrivateElements]], then throw a "DataCloneError"  DOMException.

For instance, a [[PromiseState]] or [[WeakMapData]] internal slot.


Otherwise, if value is an exotic object and value is not the  %Object.prototype% intrinsic object associated with any realm, then  throw a "DataCloneError" DOMException.

For instance, a proxy object.


Otherwise:

Set serialized to { [[Type]]: "Object", [[Properties]]: a new empty List }.
Set deep to true.

%Object.prototype% will end up being handled via this step and  subsequent steps. The end result is that its exoticness is ignored, and after deserialization  the result will be an empty object (not an immutable prototype exotic object).
Set memory[value] to  serialized.

Ifdeep is true, then:


Ifvalue has a [[MapData]] internal slot, then:
Let copiedList be a new empty List.  

For each Record { [[Key]], [[Value]] }  entryofvalue.[[MapData]]:

Let copiedEntry be a new Record { [[Key]]:  entry.[[Key]], [[Value]]: entry.[[Value]] }.
IfcopiedEntry.[[Key]] is not the special value empty, append copiedEntrytocopiedList.


For each Record { [[Key]], [[Value]] }  entryofcopiedList:

Let serializedKey be ?  StructuredSerializeInternal(entry.[[Key]], forStorage,  memory).
Let serializedValue be ?  StructuredSerializeInternal(entry.[[Value]], forStorage,  memory).
Append { [[Key]]: serializedKey,  [[Value]]: serializedValue } to serialized.[[MapData]].
 


Otherwise, if value has a [[SetData]] internal slot, then:

Let copiedList be a new empty List.  

For each entryofvalue.[[SetData]]:

Ifentry is not the special value empty, append entrytocopiedList.


For each entryofcopiedList:

Let serializedEntry be ?  StructuredSerializeInternal(entry, forStorage,  memory).
Append serializedEntrytoserialized.[[SetData]].
 


Otherwise, if value is a platform object that is a serializable object, then perform the serialization  steps for value's primary interface, given value,  serialized, and forStorage.

The serialization steps may need to perform a sub-serialization. This is an operation which takes as input a value  subValue, and returns StructuredSerializeInternal(subValue,  forStorage, memory). (In other words, a sub-serialization  is a specialization of StructuredSerializeInternal to be consistent within this  invocation.)


Otherwise, for each key in !  EnumerableOwnProperties(value, key):


If ! HasOwnProperty(value, key) is true, then:

Let inputValue be ? value.[[Get]](key,  value).
Let outputValue be ?  StructuredSerializeInternal(inputValue, forStorage,  memory).
Append { [[Key]]: key, [[Value]]:  outputValue } to serialized.[[Properties]].
 


Return serialized.
 


It's important to realize that the Records  produced by StructuredSerializeInternal might contain "pointers" to other records  that create circular references. For example, when we pass the following JavaScript object into  StructuredSerializeInternal:
 
consto= {};o.myself =o;

it produces the following result:
 
{
  [[Type]]: "Object",
  [[Properties]]: «
    {
      [[Key]]: "myself",
      [[Value]]: <a pointer to this whole structure>
    }
  »
}


2.7.4 StructuredSerialize ( value )


Return ? StructuredSerializeInternal(value, false).
 

2.7.5 StructuredSerializeForStorage ( value )


Return ? StructuredSerializeInternal(value, true).
 

2.7.6 StructuredDeserialize ( serialized, targetRealm [ ,  memory ] )


The StructuredDeserialize abstract operation takes as input a Record  serialized, which was previously produced by StructuredSerializeorStructuredSerializeForStorage, and deserializes it into a new JavaScript value,  created in targetRealm.

This process can throw an exception, for example when trying to allocate memory for the new  objects (especially ArrayBuffer objects).


Ifmemory was not supplied, let memory be an empty map.

The purpose of the memory map is to avoid deserializing objects  twice. This ends up preserving cycles and the identity of duplicate objects in graphs.

Ifmemory[serialized] exists, then  return memory[serialized].
Let deep be false.
Let value be an uninitialized value.
Ifserialized.[[Type]] is "primitive", then set valuetoserialized.[[Value]].

Otherwise, if serialized.[[Type]] is "Boolean", then set value to a  new Boolean object in targetRealm whose [[BooleanData]] internal slot value is  serialized.[[BooleanData]].
Otherwise, if serialized.[[Type]] is "Number", then set value to a  new Number object in targetRealm whose [[NumberData]] internal slot value is  serialized.[[NumberData]].
Otherwise, if serialized.[[Type]] is "BigInt", then set value to a  new BigInt object in targetRealm whose [[BigIntData]] internal slot value is  serialized.[[BigIntData]].
Otherwise, if serialized.[[Type]] is "String", then set value to a  new String object in targetRealm whose [[StringData]] internal slot value is  serialized.[[StringData]].
Otherwise, if serialized.[[Type]] is "Date", then set value to a new  Date object in targetRealm whose [[DateValue]] internal slot value is  serialized.[[DateValue]].
Otherwise, if serialized.[[Type]] is "RegExp", then set value to a  new RegExp object in targetRealm whose [[RegExpMatcher]] internal slot value is  serialized.[[RegExpMatcher]], whose [[OriginalSource]] internal slot value is  serialized.[[OriginalSource]], and whose [[OriginalFlags]] internal slot value is  serialized.[[OriginalFlags]].

Otherwise, if serialized.[[Type]] is "SharedArrayBuffer", then:

IftargetRealm's corresponding agent cluster is not  serialized.[[AgentCluster]], then throw a  "DataCloneError" DOMException.
Otherwise, set value to a new SharedArrayBuffer object in  targetRealm whose [[ArrayBufferData]] internal slot value is  serialized.[[ArrayBufferData]] and whose [[ArrayBufferByteLength]] internal slot  value is serialized.[[ArrayBufferByteLength]].


Otherwise, if serialized.[[Type]] is "GrowableSharedArrayBuffer", then:

IftargetRealm's corresponding agent cluster is not  serialized.[[AgentCluster]], then throw a  "DataCloneError" DOMException.
Otherwise, set value to a new SharedArrayBuffer object in  targetRealm whose [[ArrayBufferData]] internal slot value is  serialized.[[ArrayBufferData]], whose [[ArrayBufferByteLengthData]] internal slot  value is serialized.[[ArrayBufferByteLengthData]], and whose  [[ArrayBufferMaxByteLength]] internal slot value is  serialized.[[ArrayBufferMaxByteLength]].


Otherwise, if serialized.[[Type]] is "ArrayBuffer", then set value to a  new ArrayBuffer object in targetRealm whose [[ArrayBufferData]] internal slot value  is serialized.[[ArrayBufferData]], and whose [[ArrayBufferByteLength]] internal slot  value is serialized.[[ArrayBufferByteLength]].

If this throws an exception, catch it, and then throw a  "DataCloneError" DOMException.

This step might throw an exception if there is not enough memory available to  create such an ArrayBuffer object.


Otherwise, if serialized.[[Type]] is "ResizableArrayBuffer", then set  value to a new ArrayBuffer object in targetRealm whose [[ArrayBufferData]]  internal slot value is serialized.[[ArrayBufferData]], whose  [[ArrayBufferByteLength]] internal slot value is  serialized.[[ArrayBufferByteLength]], and whose [[ArrayBufferMaxByteLength]] internal  slot value is serialized.[[ArrayBufferMaxByteLength]].

If this throws an exception, catch it, and then throw a  "DataCloneError" DOMException.

This step might throw an exception if there is not enough memory available to  create such an ArrayBuffer object.


Otherwise, if serialized.[[Type]] is "ArrayBufferView", then:

Let deserializedArrayBuffer be ?  StructuredDeserialize(serialized.[[ArrayBufferSerialized]],  targetRealm, memory).
Ifserialized.[[Constructor]] is "DataView", then set value to a  new DataView object in targetRealm whose [[ViewedArrayBuffer]] internal slot value  is deserializedArrayBuffer, whose [[ByteLength]] internal slot value is  serialized.[[ByteLength]], and whose [[ByteOffset]] internal slot value is  serialized.[[ByteOffset]].
Otherwise, set value to a new typed array object in targetRealm,  using the constructor given by serialized.[[Constructor]], whose  [[ViewedArrayBuffer]] internal slot value is deserializedArrayBuffer, whose  [[TypedArrayName]] internal slot value is serialized.[[Constructor]], whose  [[ByteLength]] internal slot value is serialized.[[ByteLength]], whose  [[ByteOffset]] internal slot value is serialized.[[ByteOffset]], and whose  [[ArrayLength]] internal slot value is serialized.[[ArrayLength]].


Otherwise, if serialized.[[Type]] is "Map", then:

Set value to a new Map object in targetRealm whose [[MapData]]  internal slot value is a new empty List.
Set deep to true.


Otherwise, if serialized.[[Type]] is "Set", then:

Set value to a new Set object in targetRealm whose [[SetData]]  internal slot value is a new empty List.
Set deep to true.


Otherwise, if serialized.[[Type]] is "Array", then:

Let outputProtobetargetRealm.[[Intrinsics]].[[%Array.prototype%]].
Set value to ! ArrayCreate(serialized.[[Length]],  outputProto).
Set deep to true.


Otherwise, if serialized.[[Type]] is "Object", then:

Set value to a new Object in targetRealm.
Set deep to true.


Otherwise, if serialized.[[Type]] is "Error", then:

Let prototypebe%Error.prototype%.
Ifserialized.[[Name]] is "EvalError", then set prototypeto%EvalError.prototype%.
Ifserialized.[[Name]] is "RangeError", then set prototypeto%RangeError.prototype%.
Ifserialized.[[Name]] is "ReferenceError", then set  prototypeto%ReferenceError.prototype%.
Ifserialized.[[Name]] is "SyntaxError", then set prototypeto%SyntaxError.prototype%.
Ifserialized.[[Name]] is "TypeError", then set prototypeto%TypeError.prototype%.
Ifserialized.[[Name]] is "URIError", then set prototypeto%URIError.prototype%.
Let messagebeserialized.[[Message]].
Set valuetoOrdinaryObjectCreate(prototype, «  [[ErrorData]] »).
Let messageDescbePropertyDescriptor { [[Value]]:  message, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true  }.
Ifmessage is not undefined, then perform !  OrdinaryDefineOwnProperty(value, "message",  messageDesc).
Any interesting accompanying data attached to serialized should be  deserialized and attached to value.


Otherwise:

Let interfaceNamebeserialized.[[Type]].
If the interface identified by interfaceName is not  exposedintargetRealm, then throw a  "DataCloneError" DOMException.
Set value to a new instance of the interface identified by  interfaceName, created in targetRealm.

Set deep to true.

Set memory[serialized] to  value.

Ifdeep is true, then:


Ifserialized.[[Type]] is "Map", then:


For each Record { [[Key]], [[Value]] }  entryofserialized.[[MapData]]:

Let deserializedKey be ?  StructuredDeserialize(entry.[[Key]], targetRealm,  memory).
Let deserializedValue be ?  StructuredDeserialize(entry.[[Value]], targetRealm,  memory).
Append { [[Key]]: deserializedKey,  [[Value]]: deserializedValue } to value.[[MapData]].
 


Otherwise, if serialized.[[Type]] is "Set", then:


For each entryofserialized.[[SetData]]:

Let deserializedEntry be ?  StructuredDeserialize(entry, targetRealm,  memory).
Append deserializedEntrytovalue.[[SetData]].
 


Otherwise, if serialized.[[Type]] is "Array" or "Object", then:


For each Record { [[Key]], [[Value]] }  entryofserialized.[[Properties]]:

Let deserializedValue be ?  StructuredDeserialize(entry.[[Value]], targetRealm,  memory).
Let result be ! CreateDataProperty(value,  entry.[[Key]], deserializedValue).
Assert: result is true.
 


Otherwise:


Perform the appropriate deserialization steps for the interface identified  by serialized.[[Type]], given serialized, value, and  targetRealm.

The deserialization steps may need to perform a sub-deserialization. This is an operation which takes as input a  previously-serialized Record subSerialized, and returns  StructuredDeserialize(subSerialized, targetRealm,  memory). (In other words, a sub-deserialization is a specialization  of StructuredDeserialize to be consistent within this invocation.)
 


Return value.
 

2.7.7 StructuredSerializeWithTransfer ( value, transferList  )



Let memory be an empty map.

In addition to how it is used normally by  StructuredSerializeInternal, in this algorithm memory is also used to  ensure that StructuredSerializeInternal ignores items in transferList,  and let us do our own handling instead.


For each transferableoftransferList:

Iftransferable has neither an [[ArrayBufferData]] internal slot nor a  [[Detached]] internal slot, then throw a  "DataCloneError" DOMException.
Iftransferable has an [[ArrayBufferData]] internal slot and  IsSharedArrayBuffer(transferable) is true, then throw a  "DataCloneError" DOMException.
Ifmemory[transferable] exists,  then throw a "DataCloneError" DOMException.

Set memory[transferable] to {  [[Type]]: an uninitialized value }.

transferable is not transferred yet as transferring has side  effects and StructuredSerializeInternal needs to be able to throw first.
 

Let serialized be ? StructuredSerializeInternal(value,  false, memory).
Let transferDataHolders be a new empty List.

For each transferableoftransferList:

Iftransferable has an [[ArrayBufferData]] internal slot and  IsDetachedBuffer(transferable) is true, then throw a  "DataCloneError" DOMException.
Iftransferable has a [[Detached]] internal slot and  transferable.[[Detached]] is true, then throw a  "DataCloneError" DOMException.
Let dataHolderbememory[transferable].

Iftransferable has an [[ArrayBufferData]] internal slot, then:


Iftransferable has an [[ArrayBufferMaxByteLength]] internal slot, then:

Set dataHolder.[[Type]] to "ResizableArrayBuffer".
Set dataHolder.[[ArrayBufferData]] to  transferable.[[ArrayBufferData]].
Set dataHolder.[[ArrayBufferByteLength]] to  transferable.[[ArrayBufferByteLength]].
Set dataHolder.[[ArrayBufferMaxByteLength]] to  transferable.[[ArrayBufferMaxByteLength]].


Otherwise:

Set dataHolder.[[Type]] to "ArrayBuffer".
Set dataHolder.[[ArrayBufferData]] to  transferable.[[ArrayBufferData]].
Set dataHolder.[[ArrayBufferByteLength]] to  transferable.[[ArrayBufferByteLength]].


Perform ? DetachArrayBuffer(transferable).

Specifications can use the [[ArrayBufferDetachKey]] internal slot to prevent  ArrayBuffers from being detached. This is used in  WebAssembly JavaScript Interface, for example. [WASMJS]
 


Otherwise:

Assert: transferable is a platform object that is  a transferable object.
Let interfaceName be the identifier of the primary interfaceoftransferable.
Set dataHolder.[[Type]] to interfaceName.
Perform the appropriate transfer steps for the interface identified by  interfaceName, given transferable and dataHolder.
Set transferable.[[Detached]] to true.

Append dataHoldertotransferDataHolders.

Return { [[Serialized]]: serialized, [[TransferDataHolders]]:  transferDataHolders }.
 

2.7.8 StructuredDeserializeWithTransfer ( serializeWithTransferResult,  targetRealm )



Let memory be an empty map.

Analogous to StructuredSerializeWithTransfer, in addition to how it  is used normally by StructuredDeserialize, in this algorithm memory is  also used to ensure that StructuredDeserialize ignores items in  serializeWithTransferResult.[[TransferDataHolders]], and let us do our own handling  instead.

Let transferredValues be a new empty List.

For each transferDataHolderofserializeWithTransferResult.[[TransferDataHolders]]:

Let value be an uninitialized value.

IftransferDataHolder.[[Type]] is "ArrayBuffer", then set value to a  new ArrayBuffer object in targetRealm whose [[ArrayBufferData]] internal slot value  is transferDataHolder.[[ArrayBufferData]], and whose [[ArrayBufferByteLength]]  internal slot value is transferDataHolder.[[ArrayBufferByteLength]].

In cases where the original memory occupied by [[ArrayBufferData]] is  accessible during the deserialization, this step is unlikely to throw an exception, as no new  memory needs to be allocated: the memory occupied by [[ArrayBufferData]] is instead just  getting transferred into the new ArrayBuffer. This could be true, for example, when both the  source and target realms are in the same process.


Otherwise, if transferDataHolder.[[Type]] is "ResizableArrayBuffer", then set  value to a new ArrayBuffer object in targetRealm whose  [[ArrayBufferData]] internal slot value is transferDataHolder.[[ArrayBufferData]],  whose [[ArrayBufferByteLength]] internal slot value is  transferDataHolder.[[ArrayBufferByteLength]], and whose  [[ArrayBufferMaxByteLength]] internal slot value is  transferDataHolder.[[ArrayBufferMaxByteLength]].

For the same reason as the previous step, this step is also unlikely to throw  an exception.


Otherwise:

Let interfaceNamebetransferDataHolder.[[Type]].
If the interface identified by interfaceName is not exposed in  targetRealm, then throw a "DataCloneError"  DOMException.
Set value to a new instance of the interface identified by  interfaceName, created in targetRealm.
Perform the appropriate transfer-receiving steps for the interface  identified by interfaceName given transferDataHolder and  value.

Set memory[transferDataHolder] to  value.
Append valuetotransferredValues.

Let deserialized be ?  StructuredDeserialize(serializeWithTransferResult.[[Serialized]],  targetRealm, memory).
Return { [[Deserialized]]: deserialized, [[TransferredValues]]:  transferredValues }.
 

2.7.9 Performing serialization and  transferring from other specifications


Other specifications may use the abstract operations defined here. The following provides  some guidance on when each abstract operation is typically useful, with examples.

StructuredSerializeWithTransfer
StructuredDeserializeWithTransfer

Cloning a value to another realm, with a transfer list, but where the target  realm is not known ahead of time. In this case the serialization step can be performed  immediately, with the deserialization step delayed until the target realm becomes known.

messagePort.postMessage()  uses this pair of abstract operations, as the destination realm is not known until the  MessagePort has been shipped.

StructuredSerialize
StructuredSerializeForStorage
StructuredDeserialize

Creating a realm-independent snapshot of a given value which can be saved for an  indefinite amount of time, and then reified back into a JavaScript value later, possibly  multiple times.

StructuredSerializeForStorage can be used for situations where the serialization  is anticipated to be stored in a persistent manner, instead of passed between realms. It throws  when attempting to serialize SharedArrayBuffer objects, since storing shared memory  does not make sense. Similarly, it can throw or possibly have different behavior when given a  platform object with custom serialization steps when the  forStorage argument is true.

history.pushState() and history.replaceState() use  StructuredSerializeForStorage on author-supplied state objects, storing them as  serialized state in the appropriate session history entry. Then,  StructuredDeserialize is used so that the history.state property can return a clone of the  originally-supplied state object.

broadcastChannel.postMessage() uses  StructuredSerialize on its input, then uses StructuredDeserialize  multiple times on the result to produce a fresh clone for each destination being broadcast  to. Note that transferring does not make sense in multi-destination situations.

Any API for persisting JavaScript values to the filesystem would also use  StructuredSerializeForStorage on its input and StructuredDeserialize  on its output.
 

In general, call sites may pass in Web IDL values instead of JavaScript values; this is to be  understood to perform an implicit conversion to the  JavaScript value before invoking these algorithms.


Call sites that are not invoked as a result of author code synchronously calling into a user  agent method must take care to properly prepare to run script and prepare to  run a callback before invoking StructuredSerialize,  StructuredSerializeForStorage, or StructuredSerializeWithTransfer  abstract operations, if they are being performed on arbitrary objects. This is necessary because  the serialization process can invoke author-defined accessors as part of its final  deep-serialization steps, and these accessors could call into operations that rely on the entry and incumbent concepts being properly set up.

window.postMessage() performs  StructuredSerializeWithTransfer on its arguments, but is careful to do so  immediately, inside the synchronous portion of its algorithm. Thus it is able to use the  algorithms without needing to prepare to run script and prepare to run a  callback.

In contrast, a hypothetical API that used StructuredSerialize to  serialize some author-supplied object periodically, directly from a task on the event loop, would need to ensure it performs  the appropriate preparations beforehand. As of this time, we know of no such APIs on the platform;  usually it is simpler to perform the serialization ahead of time, as a synchronous consequence of  author code.

2.7.10 Structured cloning API


result = self.structuredClone(value[, { transfer }])

Takes the input value and returns a deep copy by performing the structured clone algorithm.  Transferable objects listed in the transfer array are transferred, not  just cloned, meaning that they are no longer usable in the input value.

Throws a "DataCloneError" DOMException if any part of  the input value is not serializable.
 


structuredClone
Support in all current engines.
Firefox94+Safari15.4+Chrome98+

Opera?Edge98+

Edge (Legacy)?Internet ExplorerNo

Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

The structuredClone(value,  options) method steps are:

Let serialized be ?  StructuredSerializeWithTransfer(value, options["transfer"]).
Let deserializeRecord be ?  StructuredDeserializeWithTransfer(serialized, this's  relevant realm).
Return deserializeRecord.[[Deserialized]].