活文档 — 最后更新于 2024年9月12日
为了支持传递 JavaScript 对象,包括跨越领域边界的平台对象,本规范定义了以下用于序列化和反序列化对象的框架,在某些情况下包括传输底层数据而不是复制它。总的来说,这个序列化/反序列化过程被称为“结构化克隆”,尽管大多数 API 执行单独的序列化和反序列化步骤。(一个显著的例外是structuredClone()
方法。)
本节使用 JavaScript 规范中的术语和排版约定。[JAVASCRIPT]
/developer.mozilla.org/en-US/docs/Glossary/Serializable_object
/developer.mozilla.org/en-US/docs/Glossary/Serializable_object
/developer.mozilla.org/en-US/docs/Glossary/Serializable_object
/developer.mozilla.org/en-US/docs/Glossary/Serializable_object
/developer.mozilla.org/en-US/docs/Glossary/Serializable_object
/developer.mozilla.org/en-US/docs/Glossary/Serializable_object
/developer.mozilla.org/en-US/docs/Glossary/Serializable_object
可序列化对象 支持以独立于任何给定领域的方式进行序列化,并在以后进行反序列化。这允许它们存储在磁盘上并在以后恢复,或者跨代理甚至代理集群边界进行克隆。
并非所有对象都是可序列化对象,并且并非所有可序列化对象的方面在序列化时都一定会被保留。
平台对象 可以是可序列化对象,如果它们的主接口用[Serializable]
IDL 扩展属性进行装饰。此类接口还必须定义以下算法
一组将 value 中的数据序列化到 serialized 的字段中的步骤。序列化到 serialized 中的结果数据必须独立于任何领域。
如果无法进行序列化,这些步骤可能会抛出异常。
这些步骤可能会执行子序列化来序列化嵌套的数据结构。它们不应该直接调用StructuredSerialize,因为这样做会省略重要的 memory 参数。
如果 forStorage 参数与算法无关,则这些步骤的介绍应省略对该参数的提及。
一组使用 serialized 中的数据来适当地设置 value 的步骤。value 将是正在讨论的平台对象类型的新创建的实例,其内部数据均未设置;设置这些数据是这些步骤的任务。
如果无法进行反序列化,这些步骤可能会抛出异常。
这些步骤可能会执行子反序列化来反序列化嵌套的数据结构。它们不应该直接调用StructuredDeserialize,因为这样做会省略重要的 targetRealm 和 memory 参数。
由单个平台对象的定义来确定这些步骤序列化和反序列化的数据。通常,这些步骤非常对称。
[Serializable]
扩展属性不得带任何参数,并且只能出现在接口上。它不得在接口上出现多次。
对于给定的平台对象,在(反)序列化过程中只考虑对象的主接口。因此,如果继承参与了接口的定义,则继承链中的每个用[Serializable]
注释的接口都需要定义独立的序列化步骤和反序列化步骤,包括考虑可能来自继承接口的任何重要数据。
假设我们正在定义一个平台对象 Person
,它有两个关联数据
一个名称值,它是字符串;以及
一个最好的朋友值,它是另一个 Person
实例或 null。
然后,我们可以通过用[Serializable]
扩展属性注释 Person
接口并定义以下配套算法来定义 Person
实例为可序列化对象
JavaScript 规范中定义的对象由StructuredSerialize 抽象操作直接处理。
最初,本规范定义了“可克隆对象”的概念,可以从一个领域克隆到另一个领域。但是,为了更好地指定某些更复杂情况的行为,该模型已更新为使序列化和反序列化明确。
可传递对象 支持跨代理进行传递。传递实际上是在共享对底层数据的引用并随后分离正在传递的对象的同时重新创建该对象。这对于转移昂贵资源的所有权很有用。并非所有对象都是可传递对象,并且并非所有可传递对象的方面在传递时都一定会被保留。
传递是一个不可逆且非幂等的运算。一旦对象被传递,它就不能再次传递,甚至不能再次使用。
平台对象 可以是可传递对象,如果它们的主接口用[Transferable]
IDL 扩展属性进行装饰。此类接口还必须定义以下算法
一组将 value 中的数据传递到 dataHolder 的字段中的步骤。存储在 dataHolder 中的结果数据必须独立于任何领域。
如果无法进行传递,这些步骤可能会抛出异常。
一组接收 dataHolder 中的数据并使用它来适当地设置 value 的步骤。value 将是正在讨论的平台对象类型的新创建的实例,其内部数据均未设置;设置这些数据是这些步骤的任务。
如果无法接收传递,这些步骤可能会抛出异常。
这些步骤传输哪些数据取决于各个平台对象的定义。通常,这些步骤非常对称。
扩展属性 [Transferable]
必须不带任何参数,并且只能出现在接口上。它在一个接口上不能出现多次。
对于给定的 平台对象,在传输过程中仅考虑该对象的 主接口。因此,如果继承参与了接口的定义,则继承链中的每个带有 [Transferable]
注解的接口都需要定义独立的 传输步骤 和 传输接收步骤,包括考虑可能来自继承接口的任何重要数据。
平台对象 作为 可传输对象 具有一个 [[Detached]] 内部槽。这用于确保一旦平台对象被传输,它就不能再次被传输。
JavaScript 规范中定义的对象由 StructuredSerializeWithTransfer 抽象操作直接处理。
StructuredSerializeInternal 抽象操作以 JavaScript 值 value 作为输入,并将其序列化为一种与 领域 无关的形式,此处表示为一个 记录。这种序列化形式包含了稍后在不同领域中反序列化为新的 JavaScript 值所需的所有信息。
此过程可能会抛出异常,例如在尝试序列化不可序列化的对象时。
如果未提供 memory,则令 memory 为一个空的 映射。
memory 映射的目的是避免对对象进行两次序列化。这最终会保留图中循环和重复对象的标识。
如果 memory[value] 存在,则返回 memory[value]。
令 deep 为 false。
如果 Type(value) 为 Undefined、Null、Boolean、Number、BigInt 或 String,则返回 { [[Type]]: "primitive", [[Value]]: value }。
如果 Type(value) 为 Symbol,则抛出一个 "DataCloneError
" DOMException
。
令 serialized 为一个未初始化的值。
如果 value 具有 [[BooleanData]] 内部槽,则将 serialized 设置为 { [[Type]]: "Boolean", [[BooleanData]]: value.[[BooleanData]] }。
否则,如果 value 具有 [[NumberData]] 内部槽,则将 serialized 设置为 { [[Type]]: "Number", [[NumberData]]: value.[[NumberData]] }。
否则,如果 value 具有 [[BigIntData]] 内部槽,则将 serialized 设置为 { [[Type]]: "BigInt", [[BigIntData]]: value.[[BigIntData]] }。
否则,如果 value 具有 [[StringData]] 内部槽,则将 serialized 设置为 { [[Type]]: "String", [[StringData]]: value.[[StringData]] }。
否则,如果 value 具有 [[DateValue]] 内部槽,则将 serialized 设置为 { [[Type]]: "Date", [[DateValue]]: value.[[DateValue]] }。
否则,如果 value 具有 [[RegExpMatcher]] 内部槽,则将 serialized 设置为 { [[Type]]: "RegExp", [[RegExpMatcher]]: value.[[RegExpMatcher]], [[OriginalSource]]: value.[[OriginalSource]], [[OriginalFlags]]: value.[[OriginalFlags]] }。
否则,如果 value 具有 [[ArrayBufferData]] 内部槽,则
如果 IsSharedArrayBuffer(value) 为 true,则
如果 当前设置对象 的 跨源隔离功能 为 false,则抛出一个 "DataCloneError
" DOMException
。
此检查仅在序列化时(而不是在反序列化时)需要,因为 跨源隔离功能 不会随时间变化,并且 SharedArrayBuffer
无法离开 代理集群。
如果 forStorage 为 true,则抛出一个 "DataCloneError
" DOMException
。
如果 value 具有 [[ArrayBufferMaxByteLength]] 内部槽,则将 serialized 设置为 { [[Type]]: "GrowableSharedArrayBuffer", [[ArrayBufferData]]: value.[[ArrayBufferData]], [[ArrayBufferByteLengthData]]: value.[[ArrayBufferByteLengthData]], [[ArrayBufferMaxByteLength]]: value.[[ArrayBufferMaxByteLength]], [[AgentCluster]]: 周围代理 的 代理集群 }。
否则,将 serialized 设置为 { [[Type]]: "SharedArrayBuffer", [[ArrayBufferData]]: value.[[ArrayBufferData]], [[ArrayBufferByteLength]]: value.[[ArrayBufferByteLength]], [[AgentCluster]]: 周围代理 的 代理集群 }。
否则
如果 IsDetachedBuffer(value) 为 true,则抛出一个 "DataCloneError
" DOMException
。
令 size 为 value.[[ArrayBufferByteLength]]。
令 dataCopy 为 ? CreateByteDataBlock(size)。
在分配失败时,这可能会抛出一个 RangeError
异常。
执行 CopyDataBlockBytes(dataCopy, 0, value.[[ArrayBufferData]], 0, size)。
如果 value 具有 [[ArrayBufferMaxByteLength]] 内部槽,则将 serialized 设置为 { [[Type]]: "ResizableArrayBuffer", [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size, [[ArrayBufferMaxByteLength]]: value.[[ArrayBufferMaxByteLength]] }。
否则,将 serialized 设置为 { [[Type]]: "ArrayBuffer", [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size }。
否则,如果 value 具有 [[ViewedArrayBuffer]] 内部槽,则
如果 IsArrayBufferViewOutOfBounds(value) 为 true,则抛出一个 "DataCloneError
" DOMException
。
令 buffer 为 value 的 [[ViewedArrayBuffer]] 内部槽的值。
令 bufferSerialized 为 ? StructuredSerializeInternal(buffer, forStorage, memory)。
断言:bufferSerialized.[[Type]] 为 "ArrayBuffer"、"ResizableArrayBuffer"、"SharedArrayBuffer" 或 "GrowableSharedArrayBuffer"。
如果 value 具有 [[DataView]] 内部槽,则将 serialized 设置为 { [[Type]]: "ArrayBufferView", [[Constructor]]: "DataView", [[ArrayBufferSerialized]]: bufferSerialized, [[ByteLength]]: value.[[ByteLength]], [[ByteOffset]]: value.[[ByteOffset]] }。
否则
断言:value 具有 [[TypedArrayName]] 内部槽。
将 serialized 设置为 { [[Type]]: "ArrayBufferView", [[Constructor]]: value.[[TypedArrayName]], [[ArrayBufferSerialized]]: bufferSerialized, [[ByteLength]]: value.[[ByteLength]], [[ByteOffset]]: value.[[ByteOffset]], [[ArrayLength]]: value.[[ArrayLength]] }。
否则,如果 value 具有 [[MapData]] 内部槽,则
将 serialized 设置为 { [[Type]]: "Map", [[MapData]]: 一个新的空 列表 }。
将 deep 设置为 true。
否则,如果 value 具有 [[SetData]] 内部槽,则
将 serialized 设置为 { [[Type]]: "Set", [[SetData]]: 一个新的空 列表 }。
将 deep 设置为 true。
否则,如果 value 具有 [[ErrorData]] 内部槽并且 value 不是 平台对象,则
令 name 为 ? Get(value, "name")。
如果 name 不是 "Error"、"EvalError"、"RangeError"、"ReferenceError"、"SyntaxError"、"TypeError" 或 "URIError" 中的一个,则将 name 设置为 "Error"。
令 valueMessageDesc 为 ? value.[[GetOwnProperty]]("message
")。
如果 IsDataDescriptor(valueMessageDesc) 为 false,则令 message 为 undefined,否则为 ? ToString(valueMessageDesc.[[Value]])。
将 serialized 设置为 { [[Type]]: "Error", [[Name]]: name, [[Message]]: message }。
用户代理应该将任何尚未指定的有趣伴随数据的序列化表示(特别是 stack
属性)附加到 serialized。
有关指定此数据的正在进行的工作,请参见 错误堆栈 提案。 [JSERRORSTACKS]
否则,如果 value 是一个数组奇异对象,则
令 valueLenDescriptor 为 ? OrdinaryGetOwnProperty(value, "length
")。
令 valueLen 为 valueLenDescriptor.[[Value]]。
将 serialized 设置为 { [[Type]]: "Array", [[Length]]: valueLen, [[Properties]]: 一个新的空 列表 }。
将 deep 设置为 true。
否则,如果 value 是一个 平台对象 且为 可序列化对象
如果 value 具有一个值为 true 的 [[Detached]] 内部槽,则抛出一个 "DataCloneError
" DOMException
。
令 typeString 为 value 的 主接口 的标识符。
将 serialized 设置为 { [[Type]]: typeString }。
将 deep 设置为 true。
否则,如果 value 是一个 平台对象,则抛出一个 "DataCloneError
" DOMException
。
否则,如果 IsCallable(value) 为 true,则抛出一个 "DataCloneError
" DOMException
。
否则,如果 value 具有除 [[Prototype]]、[[Extensible]] 或 [[PrivateElements]] 之外的任何内部槽,则抛出一个 "DataCloneError
" DOMException
。
例如,[[PromiseState]] 或 [[WeakMapData]] 内部槽。
否则,如果 value 是一个奇异对象,并且 value 不是与任何 领域 关联的 %Object.prototype% 内置对象,则抛出一个 "DataCloneError
" DOMException
。
例如,代理对象。
否则
将 serialized 设置为 { [[Type]]: "Object", [[Properties]]: 一个新的空 列表 }。
将 deep 设置为 true。
%Object.prototype% 将通过此步骤和后续步骤进行处理。最终结果是忽略其奇异性,并且在反序列化后结果将是一个空对象(而不是 不可变原型奇异对象)。
将 memory[value] 设置为 serialized。
如果 deep 为 true,则
如果 value 具有 [[MapData]] 内部槽,则
令 copiedList 为一个新的空 列表。
对于 value.[[MapData]] 中的每个 记录 { [[Key]], [[Value]] } entry
对于 copiedList 中的每个 记录 { [[Key]], [[Value]] } entry
令 serializedKey 为 ? StructuredSerializeInternal(entry.[[Key]], forStorage, memory)。
令 serializedValue 为 ? StructuredSerializeInternal(entry.[[Value]], forStorage, memory)。
将 { [[Key]]: serializedKey, [[Value]]: serializedValue } 追加 到 serialized.[[MapData]]。
否则,如果 value 具有 [[SetData]] 内部槽,则
令 copiedList 为一个新的空 列表。
对于 value.[[SetData]] 中的每个 entry
如果 entry 不是特殊值 empty,则将 entry 追加 到 copiedList。
对于 copiedList 中的每个 entry
令 serializedEntry 为 ? StructuredSerializeInternal(entry, forStorage, memory)。
将 serializedEntry 追加 到 serialized.[[SetData]]。
否则,如果 value 是一个 平台对象 且为 可序列化对象,则执行 value 的 主接口 的 序列化步骤,给定 value、serialized 和 forStorage。
序列化步骤 可能需要执行 子序列化。这是一个操作,它以值 subValue 作为输入,并返回 StructuredSerializeInternal(subValue, forStorage, memory)。(换句话说,子序列化 是 StructuredSerializeInternal 的一个专门化,以便在此调用中保持一致。)
否则,对于 ! EnumerableOwnProperties(value, key) 中的每个 key
如果 ! HasOwnProperty(value, key) 为 true,则
令 inputValue 为 ? value.[[Get]](key, value)。
令 outputValue 为 ? StructuredSerializeInternal(inputValue, forStorage, memory)。
将 { [[Key]]: key, [[Value]]: outputValue } 追加 到 serialized.[[Properties]]。
返回 serialized。
重要的是要认识到,StructuredSerializeInternal 生成的 记录 可能包含指向其他记录的“指针”,这些记录会创建循环引用。例如,当我们将以下 JavaScript 对象传递到 StructuredSerializeInternal 中时
const o = {};
o. myself = o;
它会生成以下结果
{ [[Type]]: "Object", [[Properties]]: « { [[Key]]: "myself", [[Value]]: <a pointer to this whole structure> } » }
返回 ? StructuredSerializeInternal(value, false)。
返回 ? StructuredSerializeInternal(value, true)。
StructuredDeserialize 抽象操作以 记录 serialized 作为输入,该记录先前由 StructuredSerialize 或 StructuredSerializeForStorage 生成,并将其反序列化为一个新的 JavaScript 值,该值在 targetRealm 中创建。
此过程可能会抛出异常,例如在尝试为新对象(尤其是 ArrayBuffer
对象)分配内存时。
如果未提供 memory,则令 memory 为一个空的 映射。
memory 映射的目的是避免对对象进行两次反序列化。这最终保留了图中循环和重复对象的标识。
如果 memory[serialized] 存在,则返回 memory[serialized]。
令 deep 为 false。
令 value 为一个未初始化的值。
如果 serialized.[[Type]] 为 "primitive",则将 value 设置为 serialized.[[Value]]。
否则,如果 serialized.[[Type]] 为 "Boolean",则将 value 设置为 targetRealm 中一个新的布尔对象,其 [[BooleanData]] 内部槽值为 serialized.[[BooleanData]]。
否则,如果 serialized.[[Type]] 为 "Number",则将 value 设置为 targetRealm 中一个新的数字对象,其 [[NumberData]] 内部槽值为 serialized.[[NumberData]]。
否则,如果 serialized.[[Type]] 为 "BigInt",则将 value 设置为 targetRealm 中一个新的 BigInt 对象,其 [[BigIntData]] 内部槽值为 serialized.[[BigIntData]]。
否则,如果 serialized.[[Type]] 为 "String",则将 value 设置为 targetRealm 中一个新的字符串对象,其 [[StringData]] 内部槽值为 serialized.[[StringData]]。
否则,如果 serialized.[[Type]] 为 "Date",则将 value 设置为 targetRealm 中一个新的日期对象,其 [[DateValue]] 内部槽值为 serialized.[[DateValue]]。
否则,如果 serialized.[[Type]] 为 "RegExp",则将 value 设置为 targetRealm 中一个新的正则表达式对象,其 [[RegExpMatcher]] 内部槽值为 serialized.[[RegExpMatcher]],其 [[OriginalSource]] 内部槽值为 serialized.[[OriginalSource]],并且其 [[OriginalFlags]] 内部槽值为 serialized.[[OriginalFlags]]。
否则,如果 serialized.[[Type]] 为 "SharedArrayBuffer",则
如果 targetRealm 对应的 代理集群 不是 serialized.[[AgentCluster]],则抛出一个 "DataCloneError
" DOMException
。
否则,将 value 设置为 targetRealm 中一个新的 SharedArrayBuffer 对象,其 [[ArrayBufferData]] 内部槽值为 serialized.[[ArrayBufferData]],其 [[ArrayBufferByteLength]] 内部槽值为 serialized.[[ArrayBufferByteLength]]。
否则,如果 serialized.[[Type]] 为 "GrowableSharedArrayBuffer",则
如果 targetRealm 对应的 代理集群 不是 serialized.[[AgentCluster]],则抛出一个 "DataCloneError
" DOMException
。
否则,将 value 设置为 targetRealm 中一个新的 SharedArrayBuffer 对象,其 [[ArrayBufferData]] 内部槽值为 serialized.[[ArrayBufferData]],其 [[ArrayBufferByteLengthData]] 内部槽值为 serialized.[[ArrayBufferByteLengthData]],其 [[ArrayBufferMaxByteLength]] 内部槽值为 serialized.[[ArrayBufferMaxByteLength]]。
否则,如果 serialized.[[Type]] 为 "ArrayBuffer",则将 value 设置为 targetRealm 中一个新的 ArrayBuffer 对象,其 [[ArrayBufferData]] 内部槽值为 serialized.[[ArrayBufferData]],其 [[ArrayBufferByteLength]] 内部槽值为 serialized.[[ArrayBufferByteLength]]。
如果这抛出异常,则捕获它,然后抛出一个 "DataCloneError
" DOMException
。
如果无法分配足够的内存来创建这样的 ArrayBuffer 对象,则此步骤可能会抛出异常。
否则,如果 serialized.[[Type]] 为 "ResizableArrayBuffer",则将 value 设置为 targetRealm 中一个新的 ArrayBuffer 对象,其 [[ArrayBufferData]] 内部槽值为 serialized.[[ArrayBufferData]],其 [[ArrayBufferByteLength]] 内部槽值为 serialized.[[ArrayBufferByteLength]],其 [[ArrayBufferMaxByteLength]] 内部槽值为 serialized.[[ArrayBufferMaxByteLength]]。
如果这抛出异常,则捕获它,然后抛出一个 "DataCloneError
" DOMException
。
如果无法分配足够的内存来创建这样的 ArrayBuffer 对象,则此步骤可能会抛出异常。
否则,如果 serialized.[[Type]] 为 "ArrayBufferView",则
令 deserializedArrayBuffer 为 ? StructuredDeserialize(serialized.[[ArrayBufferSerialized]], targetRealm, memory)。
如果 serialized.[[Constructor]] 为 "DataView",则将 value 设置为 targetRealm 中一个新的 DataView 对象,其 [[ViewedArrayBuffer]] 内部槽值为 deserializedArrayBuffer,其 [[ByteLength]] 内部槽值为 serialized.[[ByteLength]],其 [[ByteOffset]] 内部槽值为 serialized.[[ByteOffset]]。
否则,使用由 serialized.[[Constructor]] 给出的构造函数,在 targetRealm 中创建一个新的类型化数组对象,并将其 [[ViewedArrayBuffer]] 内部槽值设置为 deserializedArrayBuffer,其 [[TypedArrayName]] 内部槽值设置为 serialized.[[Constructor]],其 [[ByteLength]] 内部槽值设置为 serialized.[[ByteLength]],其 [[ByteOffset]] 内部槽值设置为 serialized.[[ByteOffset]],其 [[ArrayLength]] 内部槽值设置为 serialized.[[ArrayLength]],并将 value 设置为该对象。
否则,如果 serialized.[[Type]] 为 "Map",则
将 value 设置为 targetRealm 中一个新的 Map 对象,其 [[MapData]] 内部槽值为一个新的空 列表。
将 deep 设置为 true。
否则,如果 serialized.[[Type]] 为 "Set",则
将 value 设置为 targetRealm 中一个新的 Set 对象,其 [[SetData]] 内部槽值为一个新的空 列表。
将 deep 设置为 true。
否则,如果 serialized.[[Type]] 为 "Array",则
令 outputProto 为 targetRealm.[[Intrinsics]].[[%Array.prototype%]]。
将 value 设置为 ! ArrayCreate(serialized.[[Length]], outputProto)。
将 deep 设置为 true。
否则,如果 serialized.[[Type]] 为 "Object",则
在 targetRealm 中创建一个新的对象,并将 value 设置为该对象。
将 deep 设置为 true。
否则,如果 serialized.[[Type]] 为 "Error",则
令 prototype 为 %Error.prototype%。
如果 serialized.[[Name]] 为 "EvalError",则将 prototype 设置为 %EvalError.prototype%。
如果 serialized.[[Name]] 为 "RangeError",则将 prototype 设置为 %RangeError.prototype%。
如果 serialized.[[Name]] 为 "ReferenceError",则将 prototype 设置为 %ReferenceError.prototype%。
如果 serialized.[[Name]] 为 "SyntaxError",则将 prototype 设置为 %SyntaxError.prototype%。
如果 serialized.[[Name]] 为 "TypeError",则将 prototype 设置为 %TypeError.prototype%。
如果 serialized.[[Name]] 为 "URIError",则将 prototype 设置为 %URIError.prototype%。
令 message 为 serialized.[[Message]]。
将 value 设置为 OrdinaryObjectCreate(prototype, « [[ErrorData]] ») 。
令 messageDesc 为 PropertyDescriptor{ [[Value]]: message, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }。
如果 message 不是 undefined,则执行 ! OrdinaryDefineOwnProperty(value, "message
", messageDesc)。
应反序列化并附加到 value 上与 serialized 相关联的任何有趣的伴随数据。
否则
令 interfaceName 为 serialized.[[Type]]。
如果由 interfaceName 标识的接口未在 targetRealm 中 公开,则抛出一个 "DataCloneError
" DOMException
。
将 value 设置为在 targetRealm 中创建的由 interfaceName 标识的接口的新实例。
将 deep 设置为 true。
设置 memory[serialized] 为 value。
如果 deep 为 true,则
如果 serialized.[[Type]] 为 "Map",则
对于 serialized.[[MapData]] 中的每个 记录 { [[Key]], [[Value]] } entry
令 deserializedKey 为 ? StructuredDeserialize(entry.[[Key]], targetRealm, memory)。
令 deserializedValue 为 ? StructuredDeserialize(entry.[[Value]], targetRealm, memory)。
追加 { [[Key]]: deserializedKey, [[Value]]: deserializedValue } 到 value.[[MapData]]。
否则,如果 serialized.[[Type]] 为 "Set",则
对于 serialized.[[SetData]] 中的每个 entry
令 deserializedEntry 为 ? StructuredDeserialize(entry, targetRealm, memory)。
追加 deserializedEntry 到 value.[[SetData]]。
否则,如果 serialized.[[Type]] 为 "Array" 或 "Object",则
对于 serialized.[[Properties]] 中的每个 记录 { [[Key]], [[Value]] } entry
令 deserializedValue 为 ? StructuredDeserialize(entry.[[Value]], targetRealm, memory)。
令 result 为 ! CreateDataProperty(value, entry.[[Key]], deserializedValue)。
断言:result 为 true。
否则
对于由 serialized.[[Type]] 标识的接口,执行相应的 反序列化步骤,给定 serialized、value 和 targetRealm。
反序列化步骤可能需要执行 子反序列化。这是一个操作,它以先前序列化的 记录 subSerialized 作为输入,并返回 StructuredDeserialize(subSerialized, targetRealm, memory)。(换句话说,子反序列化 是 StructuredDeserialize 的一个特化,以在本调用中保持一致。)
返回 value。
令 memory 为一个空的 映射。
除了 StructuredSerializeInternal 通常使用的方式外,在此算法中,memory 还用于确保 StructuredSerializeInternal 忽略 transferList 中的项目,并让我们自己进行处理。
对于 transferList 中的每个 transferable
如果 transferable 既没有 [[ArrayBufferData]] 内部槽也没有 [[Detached]] 内部槽,则抛出一个 "DataCloneError
" DOMException
。
如果 transferable 有一个 [[ArrayBufferData]] 内部槽并且 IsSharedArrayBuffer(transferable) 为 true,则抛出一个 "DataCloneError
" DOMException
。
如果 memory[transferable] 存在,则抛出一个 "DataCloneError
" DOMException
。
设置 memory[transferable] 为 { [[Type]]: 未初始化的值 }。
由于传输有副作用,并且 StructuredSerializeInternal 需要能够首先抛出异常,因此 transferable 尚未传输。
令 serialized 为 ? StructuredSerializeInternal(value, false, memory)。
令 transferDataHolders 为一个新的空 列表。
对于每个 transferable 属于 transferList
如果 transferable 具有 [[ArrayBufferData]] 内部槽位并且 IsDetachedBuffer(transferable) 为真,则抛出一个 "DataCloneError
" DOMException
。
如果 transferable 具有 [[Detached]] 内部槽位并且 transferable.[[Detached]] 为真,则抛出一个 "DataCloneError
" DOMException
。
令 dataHolder 为 memory[transferable]。
如果 transferable 具有 [[ArrayBufferData]] 内部槽位,则
如果 transferable 具有 [[ArrayBufferMaxByteLength]] 内部槽位,则
将 dataHolder.[[Type]] 设置为 "ResizableArrayBuffer"。
将 dataHolder.[[ArrayBufferData]] 设置为 transferable.[[ArrayBufferData]]。
将 dataHolder.[[ArrayBufferByteLength]] 设置为 transferable.[[ArrayBufferByteLength]]。
将 dataHolder.[[ArrayBufferMaxByteLength]] 设置为 transferable.[[ArrayBufferMaxByteLength]]。
否则
将 dataHolder.[[Type]] 设置为 "ArrayBuffer"。
将 dataHolder.[[ArrayBufferData]] 设置为 transferable.[[ArrayBufferData]]。
将 dataHolder.[[ArrayBufferByteLength]] 设置为 transferable.[[ArrayBufferByteLength]]。
执行 ? DetachArrayBuffer(transferable)。
规范可以使用 [[ArrayBufferDetachKey]] 内部槽位来防止 ArrayBuffer
被分离。例如,这在 WebAssembly JavaScript 接口 中使用。 [WASMJS]
否则
追加 dataHolder 到 transferDataHolders。
返回 { [[Serialized]]: serialized, [[TransferDataHolders]]: transferDataHolders }。
令 memory 为一个空的 映射。
类似于 StructuredSerializeWithTransfer,除了它通常如何被 StructuredDeserialize 使用之外,在此算法中 memory 也用于确保 StructuredDeserialize 忽略 serializeWithTransferResult.[[TransferDataHolders]] 中的项目,并让我们自己进行处理。
令 transferredValues 为一个新的空 列表。
对于每个 transferDataHolder 属于 serializeWithTransferResult.[[TransferDataHolders]]
令 value 为一个未初始化的值。
如果 transferDataHolder.[[Type]] 为 "ArrayBuffer",则将 value 设置为 targetRealm 中的一个新的 ArrayBuffer 对象,其 [[ArrayBufferData]] 内部槽位值为 transferDataHolder.[[ArrayBufferData]],其 [[ArrayBufferByteLength]] 内部槽位值为 transferDataHolder.[[ArrayBufferByteLength]]。
在 [[ArrayBufferData]] 占据的原始内存在反序列化期间可访问的情况下,此步骤不太可能抛出异常,因为不需要分配新的内存:[[ArrayBufferData]] 占据的内存改为只是被转移到新的 ArrayBuffer 中。例如,当源和目标领域都在同一个进程中时,这可能是正确的。
否则,如果 transferDataHolder.[[Type]] 为 "ResizableArrayBuffer",则将 value 设置为 targetRealm 中的一个新的 ArrayBuffer 对象,其 [[ArrayBufferData]] 内部槽位值为 transferDataHolder.[[ArrayBufferData]],其 [[ArrayBufferByteLength]] 内部槽位值为 transferDataHolder.[[ArrayBufferByteLength]],其 [[ArrayBufferMaxByteLength]] 内部槽位值为 transferDataHolder.[[ArrayBufferMaxByteLength]]。
由于与上一步相同的原因,此步骤也不太可能抛出异常。
否则
令 interfaceName 为 transferDataHolder.[[Type]]。
如果由 interfaceName 标识的接口未在 targetRealm 中公开,则抛出一个 "DataCloneError
" DOMException
。
将 value 设置为在 targetRealm 中创建的由 interfaceName 标识的接口的新实例。
对于由 interfaceName 标识的接口,执行相应的 传输接收步骤,给定 transferDataHolder 和 value。
设置 memory[transferDataHolder] 为 value。
追加 value 到 transferredValues。
令 deserialized 为 ? StructuredDeserialize(serializeWithTransferResult.[[Serialized]], targetRealm, memory)。
返回 { [[Deserialized]]: deserialized, [[TransferredValues]]: transferredValues }。
其他规范可能会使用此处定义的抽象操作。以下提供了一些关于何时每个抽象操作通常有用的指导,并举例说明。
使用传输列表将值克隆到另一个 领域,但目标领域事先未知。在这种情况下,序列化步骤可以立即执行,反序列化步骤延迟到目标领域变得已知。
messagePort.postMessage()
使用这对抽象操作,因为目标领域直到 MessagePort
已发送 才会知道。
创建给定值的 领域 无关快照,该快照可以保存无限的时间,然后在以后可能多次重新具体化回 JavaScript 值。
StructuredSerializeForStorage 可用于预计序列化将以持久方式存储的情况,而不是在领域之间传递。当尝试序列化 SharedArrayBuffer
对象时,它会抛出异常,因为存储共享内存没有意义。类似地,当给定具有自定义 序列化步骤 的 平台对象 且 forStorage 参数为 true 时,它可能会抛出异常或可能具有不同的行为。
history.pushState()
和 history.replaceState()
在作者提供的状态对象上使用 StructuredSerializeForStorage,将它们作为 序列化状态 存储在相应的 会话历史条目 中。然后,使用 StructuredDeserialize,以便 history.state
属性可以返回原始提供的状态对象的克隆。
broadcastChannel.postMessage()
在其输入上使用 StructuredSerialize,然后在结果上多次使用 StructuredDeserialize 为每个要广播到的目标生成一个新的克隆。请注意,在多目标情况下,传输没有意义。
任何将 JavaScript 值持久保存到文件系统的 API 也将在其输入上使用 StructuredSerializeForStorage,并在其输出上使用 StructuredDeserialize。
通常,调用站点可能会传入 Web IDL 值而不是 JavaScript 值;这表示在调用这些算法之前对 JavaScript 值执行隐式 转换。
不是由于作者代码同步调用用户代理方法而导致的调用站点必须注意在调用 StructuredSerialize、StructuredSerializeForStorage 或 StructuredSerializeWithTransfer 抽象操作之前正确地 准备运行脚本 和 准备运行回调,如果它们正在对任意对象执行。这是必要的,因为序列化过程可以在其最终的深度序列化步骤中调用作者定义的访问器,并且这些访问器可能会调用依赖于 入口 和 在职者 概念正确设置的操作。
window.postMessage()
对其参数执行 StructuredSerializeWithTransfer,但会小心地在算法的同步部分立即执行。因此,它能够在无需 准备运行脚本 和 准备运行回调 的情况下使用这些算法。
相比之下,一个假设的 API 使用 StructuredSerialize 定期地从 任务(位于 事件循环 上)序列化一些作者提供的对象,则需要确保事先执行适当的准备工作。截至目前,我们不知道平台上是否有此类 API;通常,提前执行序列化作为作者代码的同步结果会更简单。
result = self.structuredClone(value[, { transfer }])
获取输入值并通过执行结构化克隆算法返回一个深拷贝。 可传输对象 列在 transfer
数组中,它们会被传输而不是仅仅克隆,这意味着它们在输入值中不再可用。
如果输入值的任何部分不可 序列化,则抛出 "DataCloneError
" DOMException
。
所有当前引擎都支持。
structuredClone(value, options)
方法的步骤如下
令 serialized 为 ? StructuredSerializeWithTransfer(value, options["transfer
"])。
令 deserializeRecord 为 ? StructuredDeserializeWithTransfer(serialized, this 的 相关领域)。
返回 deserializeRecord.[[Deserialized]]。