# Uint8Arrayのsliceとsubarrayは何が違う？Bufferも含めてコピーと共有を確認する

切り出したバイト列を変更したら元データも変わった。Uint8Arrayのslice・subarrayとNode.jsのBuffer.sliceの違い、Buffer.fromで範囲を広げてしまう例を手元で再現します。

正規URL: https://3mikan.com/archives/6089

著者: みかん
公開: 2026-09-08T15:06:00.000Z
更新: 2026-09-08T15:06:00.000Z

バイト列を別の変数へ切り出し、そこで値を変えたら元のデータまで変わった。「新しい変数に入れたから独立している」と思うと、気付きにくい不具合です。

通常の`Uint8Array`では、**`slice()`は値のコピー、`subarray()`は保存領域を共有する別の見方**を作ります。ただしNode.jsの`Buffer.slice()`はコピーではなく共有です。名前だけで判断せず、扱う型と必要な範囲を確認しましょう。

## まずは手元で再現する

[実行用サンプルを開く・保存する](/examples/byte-copy-and-view.mjs)から、`byte-copy-and-view.mjs`という名前で保存して実行してください。

```sh
node byte-copy-and-view.mjs
```

確認環境はNode.js v24.14.0です。追加パッケージ、アカウント、ネットワーク通信、ファイルの書き込みは不要です。サンプルは変更前後の値を照合し、最後に「すべての実行例が期待値と一致しました。」と表示します。以下のJavaScriptの枠も、それぞれ別の`.mjs`ファイルで実行できます。

## subarrayは、同じ保存領域の一部を見る

まず、4バイトのうち中央の2バイトを切り出します。`1, 3`は、位置1から位置3の手前までという指定です。

```js
const source = new Uint8Array([10, 20, 30, 40]);
const view = source.subarray(1, 3);
const copy = source.slice(1, 3);

view[0] = 99;
console.log([...source].join(',')); // 10,99,30,40
console.log([...view].join(','));   // 99,30
console.log([...copy].join(','));   // 20,30
console.log(view === source);        // false
console.log(view.buffer === source.buffer); // true
```

`view`と`source`は別のオブジェクトですが、バイトを置く`ArrayBuffer`を共有しています。このように、ある保存領域を指定の範囲や型で扱うものをビューと呼びます。別オブジェクトであることと、データが独立していることは同じではありません。

`subarray()`で作ったビューからの変更は元へ反映され、元からの変更もビューで見えます。一方、`slice()`の結果は切り出した時点の値を持つコピーです。[MDNのsubarray解説](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/subarray)、[sliceの解説](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/slice)

実行用サンプルでは、さらに`source[2] = 77`と変更し、`view`だけが変わって`copy`は`20,30`のまま残ることを確認しています。

## Buffer.sliceはコピーではない

同じ`slice()`という名前でも、Node.jsの`Buffer`では動作が異なります。

```js
import { Buffer } from 'node:buffer';

const source = Buffer.from([10, 20, 30]);
const view = source.slice(1);
const copy = Buffer.from(view);
view[0] = 99;

console.log([...source].join(',')); // 10,99,30
console.log([...copy].join(','));   // 20,30
console.log(source instanceof Uint8Array); // true
```

`Buffer`は`Uint8Array`の一種ですが、`slice()`は従来との互換性のために保存領域を共有します。コピーしたい場合、この例では`Buffer.from(view)`を使っています。共有する意図を表すなら、`Buffer`でも`subarray()`を使えます。[Node.jsのBufferとTypedArrayの違い](https://nodejs.org/download/release/v24.14.0/docs/api/buffer.html#buffers-and-typedarrays)

そのため、`instanceof Uint8Array`が`true`だから`slice()`も必ずコピーになる、と判断することはできません。

## .bufferだけを渡すと、見ていた範囲より広くなる

もう一つの落とし穴は、範囲を切り出した後の`.buffer`です。これは切り出した部分だけを持つ箱ではなく、元の保存領域を指している場合があります。

```js
import { Buffer } from 'node:buffer';

const owner = new Uint8Array([17, 34, 51, 68]);
const window = owner.subarray(1, 3);
const all = Buffer.from(window.buffer);
const ranged = Buffer.from(window.buffer, window.byteOffset, window.byteLength);
const independent = Buffer.from(window);

console.log([...all].join(','));         // 17,34,51,68
console.log([...ranged].join(','));      // 34,51
console.log([...independent].join(',')); // 34,51

owner[1] = 9;
console.log([...ranged].join(','));      // 9,51
console.log([...independent].join(',')); // 34,51
```

`Buffer.from(window.buffer)`にはビューの開始位置や長さを渡していません。そのため、今回の例では元の4バイト全体を扱う結果になります。

`byteOffset`と`byteLength`を渡せば、共有する範囲を中央の2バイトに限定できます。ただし、範囲を正しく指定してもコピーにはなりません。最後の出力が示すように、元の変更は引き続き反映されます。[Buffer.fromのArrayBuffer入力](https://nodejs.org/download/release/v24.14.0/docs/api/buffer.html#static-method-bufferfromarraybuffer-byteoffset-length)

## 「どの範囲か」と「独立させるか」を別々に決める

元の現在値を見続けたいなら共有、変更前の値を比較用に残したいなら、その時点でコピーする必要があります。たとえば後でハッシュの入力を比較する場合、共有ビューを保存しただけでは、変更前の値を残したことにはなりません。

まず必要なバイト範囲を決め、次に元の更新を反映させるかを決める。この二つを分けると、余分なバイトを渡す問題と、元の変更が伝わる問題を別々に直せます。

この記事は`Uint8Array`と`Buffer`の8ビットの値を扱う例です。文字をどのバイト列へ変換するかは、[文字列とUTF-8のバイト列](/archives/6074)のような別の問題です。文字の表現、扱う範囲、コピーか共有かを混同しないようにしましょう。

## 確認した一次情報

- [MDN: TypedArray.prototype.slice](<https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/slice>): 確認日 2026-09-08
- [MDN: TypedArray.prototype.subarray](<https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/subarray>): 確認日 2026-09-08
- [Node.js v24.14.0: BufferとTypedArray、コピーと保存領域の共有](<https://nodejs.org/download/release/v24.14.0/docs/api/buffer.html>): 確認日 2026-09-08
