JavaScript 형식화 배열
JavaScript 형식화 배열(typed array)은 배열같은 객체이고 원시(raw) 이진 데이터에 액세스하기 위한 메커니즘을 제공합니다. 이미 아시다시피, Array
객체는 동적으로 늘었다 줄고 어떤 JavaScript 값이든 가질 수 있습니다. JavaScript 엔진은 이러한 배열이 빨라지도록 최적화를 수행합니다. 그러나, audio 및 video 조작과 같은 기능 추가, WebSocket을 사용한 원시 데이터에 액세스 등 웹 어플리케이션이 점점 더 강력해짐에 따라, 빠르고 쉽게 형식화 배열의 원시 이진 데이터를 조작할 수 있게 하는 것이 JavaScript 코드에 도움이 될 때가 있음이 분명해 졌습니다.
그러나, 형식화 배열은 보통 배열과 혼동되지는 않습니다, 형식화 배열에 Array.isArray()
호출은 false
를 반환하기에. 게다가, 보통 배열에 이용할 수 있는 모든 메서드가 형식화 배열에 의해 지원되지는 않습니다(가령 push 및 pop).
버퍼 및 뷰: 형식화 배열 구조
최대 유연성 및 효율을 달성하기 위해, JavaScript 형식화 배열은 구현을 버퍼 및 뷰로 나눕니다. 버퍼 (ArrayBuffer
객체에 의해 구현됨)는 데이터 부분(chunk, 덩어리)을 나타내는 객체입니다; 이야기 할 형식이 없으며, 그 콘텐츠에 액세스하기 위한 메커니즘을 제공하지 않습니다. 버퍼에 포함된 메모리에 액세스하기 위해, 뷰를 사용할 필요가 있습니다. 뷰는 문맥(context, 즉 데이터 형, 시작 오프셋 및 요소 수)을 제공해 데이터를 실제 형식화 배열로 바꿉니다.
ArrayBuffer
ArrayBuffer
는 일반 고정 길이 이진 데이터 버퍼를 나타내는 데 사용되는 데이터 형입니다. ArrayBuffer
콘텐츠를 직접 조작할 수는 없습니다; 대신에, 형식화 배열 뷰 또는 특정 형식으로 버퍼를 나타내는 DataView
를 만들어 버퍼의 콘텐츠를 읽고 쓰기 위해 사용합니다.
형식화 배열 뷰
형식화 배열 뷰는 자체 설명형 이름이 있으며 Int8
, Uint32
, Float64
등과 같은 모든 일반 숫자 형을 위한 뷰를 제공합니다. 특별한 형식화 배열 뷰가 하나 있습니다, Uint8ClampedArray
. 값은 0에서 255 사이로 단속(제한)합니다. 예를 들어, 이는 Canvas 데이터 처리에 유용합니다.
Type | Value Range | Size in bytes | Description | Web IDL type | Equivalent C type |
---|---|---|---|---|---|
Int8Array |
-128 to 127 |
1 | 8-bit two's complement signed integer | byte |
int8_t |
Uint8Array |
0 to 255 |
1 | 8-bit unsigned integer | octet |
uint8_t |
Uint8ClampedArray |
0 to 255 |
1 | 8-bit unsigned integer (clamped) | octet |
uint8_t |
Int16Array |
-32768 to 32767 |
2 | 16-bit two's complement signed integer | short |
int16_t |
Uint16Array |
0 to 65535 |
2 | 16-bit unsigned integer | unsigned short |
uint16_t |
Int32Array |
-2147483648 to 2147483647 |
4 | 32-bit two's complement signed integer | long |
int32_t |
Uint32Array |
0 to 4294967295 |
4 | 32-bit unsigned integer | unsigned long |
uint32_t |
Float32Array |
-3.4E38 to 3.4E38 and 1.2E-38 is the min positive number |
4 | 32-bit IEEE floating point number (7 significant digits e.g., 1.123456 ) |
unrestricted float |
float |
Float64Array |
-1.8E308 to 1.8E308 and 5E-324 is the min positive number |
8 | 64-bit IEEE floating point number (16 significant digits e.g., 1.123...15 ) |
unrestricted double |
double |
BigInt64Array |
-2^63 to 2^63 - 1 |
8 | 64-bit two's complement signed integer | bigint |
int64_t (signed long long) |
BigUint64Array |
0 to 2^64 - 1 |
8 | 64-bit unsigned integer | bigint |
uint64_t (unsigned long long) |
DataView
DataView
는 버퍼에 임의 데이터를 읽고 쓰기 위해 getter/setter API를 제공하는 저레벨 인터페이스입니다. 예를 들어, 서로 다른 유형의 데이터를 처리하는 경우 유용합니다. 형식화 배열 뷰는 플랫폼의 본디(native) 바이트 순서(byte-order, Endianness 참조)에 속합니다. DataView
로 바이트 순서를 제어할 수 있습니다. 기본으로 big-endian이고 getter/setter 메서드로 little-endian으로 설정될 수 있습니다.
형식화 배열을 사용하는 웹 API
FileReader.prototype.readAsArrayBuffer()
-
FileReader.prototype.readAsArrayBuffer()
메서드는 지정된Blob
또는File
의 콘텐츠를 읽기 시작합니다. XMLHttpRequest.prototype.send()
-
XMLHttpRequest
인스턴스의send()
메서드는 이제 형식화 배열 및 인수로ArrayBuffer
객체를 지원합니다. ImageData.data
-
0
에서255
까지 정수값으로 RGBA 순 데이터를 포함하는 1차원 배열을 나타내는Uint8ClampedArray
입니다.
예
버퍼와 뷰 사용하기
우선, 여기서 16바이트 고정 길이로 버퍼를 만들어야 합니다:
var buffer = new ArrayBuffer(16);
이 시점에서, 바이트가 모두 0으로 미리 초기화된 메모리 덩어리가 있습니다. 그렇지만 이거 가지고 할 수 있는 게 많지 않습니다. 실제 16바이트 길이인지 확인할 수 있고 대략 그런 정도입니다:
if (buffer.byteLength === 16) {
console.log("Yes, it's 16 bytes.");
} else {
console.log("Oh no, it's the wrong size!");
}
정말 이 버퍼로 작업할 수 있기 전에, 뷰를 만들어야 합니다. 32비트 부호있는 정수 배열로 버퍼의 데이터를 다루는 뷰를 만듭시다:
var int32View = new Int32Array(buffer);
이제 보통 배열처럼 배열 내 필드에 액세스할 수 있습니다:
for (var i = 0; i < int32View.length; i++) {
int32View[i] = i * 2;
}
이는 배열의 4항목을 값 0, 2, 4 및 6으로 채웁니다 (4항목이 각각 4바이트에 총 16바이트가 됩니다).
같은 데이터에 여러 뷰
상황은 당신이 같은 데이터에 여러 뷰를 만들 수 있음을 고려하는 경우 정말 흥미로워지기 시작합니다. 예를 들어, 위 코드가 주어지면 다음과 같이 계속할 수 있습니다:
var int16View = new Int16Array(buffer);
for (var i = 0; i < int16View.length; i++) {
console.log("Entry " + i + ": " + int16View[i]);
}
여기서 우리는 기존 32비트 뷰와 같은 버퍼를 공유하는 16비트 정수 뷰를 만들고 16비트 정수로 버퍼 내 모든 값을 출력합니다. 이제 우리는 출력 0, 0, 2, 0, 4, 0, 6, 0을 얻습니다.
그렇지만 한 단계 더 앞으로 갈 수 있습니다. 다음을 생각해 보세요:
int16View[0] = 32;
console.log("Entry 0 in the 32-bit array is now " + int32View[0]);
이로부터 출력은 "Entry 0 in the 32-bit array is now 32"입니다. 즉, 두 배열은 확실히 그저 같은 데이터 버퍼 상의 뷰입니다, 버퍼를 서로 다른 형식으로 다루는. 모든 뷰 유형으로 이를 할 수 있습니다.
복잡한 데이터 구조(체)와 작업하기
단일 버퍼를 서로 다른 형인 여러 뷰(버퍼 내 서로 다른 오프셋에서 시작하는)와 결합시켜, 여러 데이터 형을 포함하는 데이터 객체와 상호 작용할 수 있습니다. 예를 들어, 이는 WebGL, 데이터 파일 또는 js-ctypes를 쓰는 동안 사용해야 하는 C 구조체에서 복잡한 데이터 구조와 상호 작용케 합니다.
이 C 구조체를 생각해 보세요:
struct someStruct {
unsigned long id;
char username[16];
float amountDue;
};
이 같은 형식으로 데이터를 포함하는 버퍼에 액세스할 수 있습니다:
var buffer = new ArrayBuffer(24);
// ... 버퍼 내의 데이터를 읽어들임 ...
var idView = new Uint32Array(buffer, 0, 1);
var usernameView = new Uint8Array(buffer, 4, 16);
var amountDueView = new Float32Array(buffer, 20, 1);
그런 뒤, 예를 들어 amountDueView[0]
로 지불할 요금(amount due)을 액세스할 수 있습니다.
참고 : C 구조체에서 데이터 구조 정렬은 플랫폼에 의존합니다. 이러한 패딩(padding) 차이에 대해 조심하고 고려하세요.
보통 배열로 변환
형식화 배열을 처리한 뒤, 때때로 Array
프로토타입의 도움을 받기 위해 보통 배열로 다시 변환하는 게 유용합니다. 이는 Array.from
또는 Array.from
이 지원되지 않는 경우 다음 코드를 사용하여 수행할 수 있습니다.
var typedArray = new Uint8Array([1, 2, 3, 4]),
normalArray = Array.prototype.slice.call(typedArray);
normalArray.length === 4;
normalArray.constructor === Array;