このページはコミュニティーの尽力で英語から翻訳されました。MDN Web Docsコミュニティーについてもっと知り、仲間になるにはこちらから。
移譲可能オブジェクト
移譲可能オブジェクト (Transferable objects) は、あるコンテキストから別のコンテキストへ移譲 することができるリソースを自分自身で所有するオブジェクトで、そのリソースが一度に 1 つのコンテキストでのみ利用できることを保証するものです。移譲が終わると、元のオブジェクトは使えなくなります。移譲されたリソースをもう指しておらず、オブジェクトの読み書きをしようとすると例外が発生します。
移譲可能なオブジェクトは一般的に、一度に単一のJavaScriptスレッドにしか安全に公開されないリソースを共有するために使用されます。例えば、ArrayBuffer はメモリーブロックを自分自身で所有する移譲可能なオブジェクトです。このようなバッファーがスレッド間で移譲されるとき、関連するメモリーリソースは元のバッファーから切り離され、新しいスレッドで作成されたバッファーオブジェクトに装着されます。元のスレッドのバッファーオブジェクトは、もはやメモリリソースを所有していないため、使用できなくなります。
移譲はstructuredClone() でオブジェクトのディープコピーを作成するときにも使用されるかもしれません。複製操作の後に、移譲されたリソースは、コピーされたオブジェクトにコピーされるのではなく、移動されます。
オブジェクトのリソースを移譲するために使用されるメカニズムは、オブジェクトに依存します。例えば、ArrayBuffer がスレッド間で移譲されるとき、それが指すメモリーリソースは高速で効率の良いゼロコピー操作により、文字通りコンテキスト間で移動されます。他にも、関連するリソースをコピーして、古いコンテキストから削除することで、オブジェクトを移譲することができる場合もあります。
すべてのオブジェクトが移譲可能なわけではありません。移譲可能なオブジェクトの一覧は下記の通りです。
In this article
オブジェクトのスレッド間の移譲
以下のコードは、メインスレッドからウェブワーカースレッドにメッセージを送信する際に、移譲がどのように動作するのかを示しています。Uint8Array は、バッファーが移譲されている間、ワーカー内でコピーされます(複製されます)。移譲後、メインスレッドからuInt8Array を読み書きしようとするとエラーが発生しますが、byteLength を調べるとゼロになったことが確認できます。
// 8MB の "file" を作成して中身を埋めます。 8MB = 1024 * 1024 * 8 Bconst uInt8Array = new Uint8Array(1024 * 1024 * 8).map((v, i) => i);console.log(uInt8Array.byteLength); // 8388608// 下層のバッファーからワーカーに移譲するworker.postMessage(uInt8Array, [uInt8Array.buffer]);console.log(uInt8Array.byteLength); // 0メモ:型付き配列Int32Array やUint8Array はシリアライズ可能ですが、移譲は行えません。しかし、その下にあるバッファーはArrayBuffer であり、これは移譲可能なオブジェクトです。data 引数にuInt8Array.buffer を設定すれば、移譲する配列にuInt8Array がなくても、送ることができます。
複製操作中の移譲について
以下のコードは、structuredClone() 操作で、基礎となるバッファーが元のオブジェクトから複製にコピーされる様子を示しています。
const original = new Uint8Array(1024);const clone = structuredClone(original);console.log(original.byteLength); // 1024console.log(clone.byteLength); // 1024original[0] = 1;console.log(clone[0]); // 0// Uint8Array を移譲すると、移譲可能なオブジェクトではないため、例外が発生します。// const transferred = structuredClone(original, {transfer: [original]});// Uint8Array.buffer ならば移譲できます。const transferred = structuredClone(original, { transfer: [original.buffer] });console.log(transferred.byteLength); // 1024console.log(transferred[0]); // 1// Uint8Array.buffer は移譲した後は使用することができません。console.log(original.byteLength); // 0対応しているオブジェクト
各種仕様書が示す移譲可能な項目は以下の通りです。
ArrayBufferMessagePortReadableStreamWritableStreamTransformStreamAudioDataImageBitmapVideoFrameOffscreenCanvasRTCDataChannel
ブラウザーの対応は、それぞれのオブジェクトの互換性情報のtransferable サブ機能で示す必要があります(例としてRTCDataChannel を参照してください)。この記事を書いている時点では、すべての移譲可能なオブジェクトでこの情報が更新されているわけではありません。
メモ:移譲可能なオブジェクトはWeb IDL ファイル の中で[Transferable] という属性でマークアップされています。