型付き配列は、C言語の配列のように要素数を予め決め全ての要素の型が同じオブジェクトなインデックス可能なコレクションです。
[https://paiza.io/projects/IAZat2VMk0xU8oOmfdEK1w?language=javascript 型付き配列を使って浮動小数点数のビットパターンを表示する]:
const f64 = new Float64Array([0, 1, NaN, Infinity, -Infinity, Math.PI])
const b64 = new BigUint64Array(f64.buffer)
f64.forEach((x, i) =>
  console.log(`${(""+x).padStart(17," ")}: ${b64[i].toString(2).padStart(64, "0")}`) )
実行結果:
                0: 0000000000000000000000000000000000000000000000000000000000000000
                1: 0011111111110000000000000000000000000000000000000000000000000000
              NaN: 0111111111111000000000000000000000000000000000000000000000000000
         Infinity: 0111111111110000000000000000000000000000000000000000000000000000
        -Infinity: 1111111111110000000000000000000000000000000000000000000000000000
3.141592653589793: 0100000000001001001000011111101101010100010001000010110100011000
行ごとに説明しましょう。
6種類のIEEE 754の64ビット倍精度浮動小数点数のバイナリ表現を表示したもので、TypedArray を使うと内部表現へのアクセスが buffer プロパティを使って可能になる事を示す例となっています。
この様な ArrayBuffer を扱うためには、DataViewオブジェクトが用意されていますが今回は同じ64ビット同士にすることでエンディアンの問題を回避しました。
TypedArray はコンストラクターとしては呼び出さず、Float64ArrayオブジェクトやBigUint64Arrayオブジェクトのような TypedArray オブジェクトのインスタンスを生成します。
// TypedArray オブジェクトの生成例
// Int8Array: 8ビット符号付き整数のTypedArrayオブジェクト
const int8Array = new Int8Array(4);
console.log(int8Array); // Int8Array [ 0, 0, 0, 0 ]
// Uint8Array: 8ビット符号なし整数のTypedArrayオブジェクト
const uint8Array = new Uint8Array([1, 2, 3, 4]);
console.log(uint8Array); // Uint8Array [ 1, 2, 3, 4 ]
// Uint8ClampedArray: オーバーフロー対策された8ビット符号なし整数のTypedArrayオブジェクト
const uint8ClampedArray = new Uint8ClampedArray([300, -50, 1000, 0]);
console.log(uint8ClampedArray); // Uint8ClampedArray [ 255, 0, 255, 0 ]
// Int16Array: 16ビット符号付き整数のTypedArrayオブジェクト
const int16Array = new Int16Array([32767, -32768, 0, 12345]);
console.log(int16Array); // Int16Array [ 32767, -32768, 0, 12345 ]
// Uint16Array: 16ビット符号なし整数のTypedArrayオブジェクト
const uint16Array = new Uint16Array([65535, 0, 1234, 9876]);
console.log(uint16Array); // Uint16Array [ 65535, 0, 1234, 9876 ]
// Int32Array: 32ビット符号付き整数のTypedArrayオブジェクト
const int32Array = new Int32Array([-2147483648, 0, 2147483647, 123456]);
console.log(int32Array); // Int32Array [ -2147483648, 0, 2147483647, 123456 ]
// Uint32Array: 32ビット符号なし整数のTypedArrayオブジェクト
const uint32Array = new Uint32Array([4294967295, 0, 987654321, 555555]);
console.log(uint32Array); // Uint32Array [ 4294967295, 0, 987654321, 555555 ]
// Float32Array: 32ビット単精度浮動小数点数のTypedArrayオブジェクト
const float32Array = new Float32Array([3.14, -0.5, 1.234, 7.89]);
console.log(float32Array); // Float32Array [ 3.140000104904175, -0.5, 1.2340000867843628, 7.889999866485596 ]
// Float64Array: 64ビット倍精度浮動小数点数のTypedArrayオブジェクト
const float64Array = new Float64Array([1.2345678901234567, -9876543.210, 0, 12345.6789]);
console.log(float64Array); // Float64Array [ 1.2345678901234567, -9876543.21, 0, 12345.6789 ]
// BigInt64Array: 64ビット符号付き整数のTypedArrayオブジェクト
const bigInt64Array = new BigInt64Array([BigInt('-9223372036854775808'), BigInt('0'), BigInt('9223372036854775807')]);
console.log(bigInt64Array); // BigInt64Array [ -9223372036854775808n, 0n, 9223372036854775807n ]
// BigUint64Array: 64ビット符号なし整数のTypedArrayオブジェクト
const bigUint64Array = new BigUint64Array([BigInt('18446744073709551615'), BigInt('0'), BigInt('1234567890123456789')]);
console.log(bigUint64Array); // BigUint64Array [ 18446744073709551615n, 0n, 1234567890123456789n ]
オーバーフロー対策と丸め処理を施された8ビット符号なし整数のTypedArray オブジェクト。
以下の変換規則に従います[https://tc39.es/ecma262/#sec-touint8clamp ECMA-262::7.1.12 ToUint8Clamp ( argument )]
{{コラム|最近接偶数丸め|2=最近接偶数丸め（round to even）は、丸めるべき値が整数部と小数部の境界にある場合、最も近い偶数に丸めるルールです。このルールは、統計的な観点から生まれており、偶数と奇数が均等に分布しているため、統計的な誤差を最小限に抑える効果があります。
最近接偶数丸めは、以下のような特徴があります
丸めるべき値が整数の場合は、そのままの整数に丸められます。
丸めるべき値が小数で、小数部が0.5の場合は、最も近い偶数に丸められます。例えば、0.5は0に、1.5は2に丸められます。
小数部が0.5である場合、整数部が偶数の場合は最も近い偶数に、整数部が奇数の場合は最も近い奇数に丸められます。
この丸め方は、統計的に誤差を最小化する効果があるため、科学計算や金融などの精度が求められる分野でよく使用されます。JavaScriptのUint8ClampedArrayの仕様においても、最近接偶数丸めが適用されています。
Canvas#imageData へのアクセスを使った高速化の節に、CanvasのimageDate.data（Uint8ClampedArrayオブジェクト）を直接操作して高速化をはかる例がある。
const ui8c = new Uint8ClampedArray(1);
[ NaN, +0, -0, -1, 256, 0.5, 0.5000000000001, 1.5, 1.5000000000001, ].forEach(x => console.log(ui8c[0] = x, ui8c[0]));
/*
 NaN 0
 0 0
 -0 0
 -1 0
 256 255
 0.5 0
 0.5000000000001 1
 */
TypedArray オブジェクトは、通常の JavaScript 配列と同様に、さまざまなメソッドを提供しています。以下は、主な TypedArray オブジェクトのメソッドのいくつかです。
共通のメソッド:
配列の要素数を返します。
const int32Array = new Int32Array([1, 2, 3, 4]);
console.log(int32Array.length); // 4
部分的な TypedArray を新しい TypedArray として返します。
const int32Array = new Int32Array([1, 2, 3, 4]);
const subArray = int32Array.subarray(1, 3);
console.log(subArray); // Int32Array [ 2, 3 ]
部分的な TypedArray のコピーを新しい TypedArray として返します。
const int32Array = new Int32Array([1, 2, 3, 4]);
const slicedArray = int32Array.slice(1, 3);
console.log(slicedArray); // Int32Array [ 2, 3 ]
数値演算関連のメソッド:
配列の各要素に対してコールバック関数を適用し、単一の累積値を返します。
const float64Array = new Float64Array([1.5, 2.5, 3.5]);
const sum = float64Array.reduce((acc, val) => acc + val, 0);
console.log(sum); // 7.5
配列の各要素に対してコールバック関数を適用し、新しい TypedArray を返します。
const int8Array = new Int8Array([1, 2, 3]);
const squaredArray = int8Array.map(val => val * val);
console.log(squaredArray); // Int8Array [ 1, 4, 9 ]
配列の各要素に対してフィルタリングを行い、新しい TypedArray を返します。
const int16Array = new Int16Array([10, -5, 7, -3]);
const positiveNumbers = int16Array.filter(val => val > 0);
console.log(positiveNumbers); // Int16Array [ 10, 7 ]
ArrayとTypedArrayにはいくつかのメソッドが共通して存在しますが、いくつかの違いもあります。以下に、ArrayにあってTypedArrayにないメソッド、あるいはその逆をいくつか挙げます。
TypedArrayには concat() メソッドが存在しません。代わりに TypedArray では、スプレッド構文を使用して結合します。
// Arrayの例
const array1 = [1, 2, 3];
const array2 = [4, 5, 6];
const newArray = array1.concat(array2);
console.log(newArray); // [1, 2, 3, 4, 5, 6]
// TypedArrayの例
const typedArray1 = new Int32Array([1, 2, 3]);
const typedArray2 = new Int32Array([4, 5, 6]);
const newTypedArray = new Int32Array([...typedArray1, ...typedArray2]);
console.log(newTypedArray); // Int32Array [1, 2, 3, 4, 5, 6]
TypedArrayには reverse() メソッドが存在しません。代わりに、スプレッド構文や独自の方法を使用して逆順にします。
// Arrayの例
const array = [1, 2, 3];
const reversedArray = array.reverse();
console.log(reversedArray); // [3, 2, 1]
// TypedArrayの例
const typedArray = new Int32Array([1, 2, 3]);
const reversedTypedArray = new Int32Array([...typedArray].reverse());
console.log(reversedTypedArray); // Int32Array [3, 2, 1]
TypedArrayは set() メソッドを提供しており、一方のTypedArrayから別のTypedArrayにデータをコピーできます。
const sourceTypedArray = new Int32Array([1, 2, 3]);
const targetTypedArray = new Int32Array(3);
// TypedArrayからTypedArrayにデータをコピー
targetTypedArray.set(sourceTypedArray);
console.log(targetTypedArray); // Int32Array [1, 2, 3]
TypedArrayは subarray() メソッドを提供しており、一部のTypedArrayを新しいTypedArrayとして取り出すことができます。
const typedArray = new Int32Array([1, 2, 3, 4, 5]);
// TypedArrayから一部を取り出す
const subArray = typedArray.subarray(1, 4);
console.log(subArray); // Int32Array [2, 3, 4]
このように、ArrayとTypedArrayは基本的な操作においては共通のメソッドを持っていますが、細かな違いがあります。注意して使用することが重要です。
TypedArray オブジェクトでもスプレッド構文を使用することができます。スプレッド構文は、配列や TypedArray オブジェクトから要素を取り出して新しい配列や TypedArray を作成するための便利な構文です。
以下は、スプレッド構文を使用した TypedArray の例です：
// TypedArray オブジェクトの作成
const sourceArray = new Int32Array([1, 2, 3, 4]);
// スプレッド構文を使用して新しい TypedArray を作成
const newArray = new Int32Array([...sourceArray, 5, 6]);
console.log(newArray); // Int32Array [ 1, 2, 3, 4, 5, 6 ]
この例では、既存の sourceArray から要素を取り出し、新しい Int32Array オブジェクト newArray を作成しています。このようにして、スプレッド構文を用いて TypedArray の要素を効果的に結合できます。ただし、スプレッド構文を使用して TypedArray のコピーを作成する場合、パフォーマンスに注意する必要があります。
TypedArrayはバイナリデータを効率的に扱うために設計されたJavaScriptの機能であり、主に以下のような場面で活用されます。
const canvas = document.getElementById('myCanvas');
const context = canvas.getContext('2d');
const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
const pixelData = new Uint8ClampedArray(imageData.data.buffer);
// pixelDataを操作して画像処理を行う
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const buffer = audioContext.createBuffer(1, 44100, 44100); // 1チャンネル, サンプリングレート44100
const audioData = new Float32Array(buffer.getChannelData(0));
// audioDataを操作して音声処理を行う
fetch('https://example.com/data.bin')
  .then(response => response.arrayBuffer())
  .then(data => {
    const dataArray = new Uint8Array(data);
    // dataArrayを解析してデータ処理を行う
  });
const vertexData = new Float32Array([/* ... */]);
const indexData = new Uint16Array([/* ... */]);
// WebGLでvertexDataやindexDataを扱う
大規模なデータ処理: 大規模なデータセットやバイナリデータの処理において、TypedArrayはメモリ効率や演算速度の向上を提供します。これにより、数値計算が必要なアプリケーションやライブラリでパフォーマンスが向上します。
これらはTypedArrayが有用である一部のシーンであり、バイナリデータを効率的に取り扱うために欠かせない機能です。
TypedArrayを使用する際には、いくつかのベストプラクティスに従うことが重要です。以下は、TypedArrayの効果的な使用に関する一般的なベストプラクティスです。
型付き配列の正確なサイズを把握する: TypedArrayは固定サイズのバッファを持ちます。事前にデータサイズを正確に知っておくことが重要です。サイズを過小評価するとデータが失われる可能性があり、過大評価すると余分なメモリを使用することになります。
// バッファを再利用する例
const buffer = new ArrayBuffer(8);
const int32Array = new Int32Array(buffer);
// 以後、同じバッファを再利用して異なるTypedArrayを作成
const float32Array = new Float32Array(buffer);
const int32Array = new Int32Array([1, 2, 3, 4]);
const dataView = new DataView(int32Array.buffer);
const firstValue = dataView.getInt32(0, true); // リトルエンディアン
const byteArray = new Uint8Array([1, 2, 3, 4, 5]);
const uint32Array = new Uint32Array(byteArray.buffer);
console.log(uint32Array); // Uint32Array [ 67305985 ]
パフォーマンスの最適化: TypedArrayは高速な数値演算を提供しますが、無駄なメモリのコピーを避けるために注意が必要です。スプレッド構文やArray.from()を使うと、性能が低下する可能性があります。
// パフォーマンスの観点からは避けるべき例
const typedArray = new Float32Array([1, 2, 3]);
const newArray = new Float32Array([...typedArray]); // 非効率的なコピー
// 直接TypedArrayのコンストラクタを使用し効率に配慮した例。
const typedArray = new Float32Array([1, 2, 3]);
const newArray = new Float32Array(typedArray.buffer, typedArray.byteOffset, typedArray.length);
これらのベストプラクティスを踏まえてTypedArrayを使うことで、メモリの効率を向上させ、高速なバイナリデータ処理を実現できます。