Los vectores JavaScript tipados son objetos tipo Array y proveen un mecanismo para el acceso a datos binarios puros. Como sabes, los objetos del tipo Array
crecen y decrecen dinámicamente y pueden tener cualquier valor JavaScript. Los motores JavaScript realizan optimizaciones y así estos vectores son veloces. Sin embargo, como las aplicaciones web se volvieron más y más poderosas, añadiendo funcionalidades tales como la manipulación de audio y video, acceso a datos puros usando WebSockets, etcétera, ha quedado claro que hay ocasiones en que sería beneficioso para el código JavaScript tener la posibilidad de, rápida y fácilmente, manipular datos binarios puros en vectores tipados.
Sin embargo, los vectores tipados no deben ser confundidos con vectores normales, al llamar Array.isArray()
en un vector tipado retorna false
. Además, no todos los metodos disponibles para vectores normales son mantenidos por los vectores tipados (p. ej. push y pop).
Buffers y vistas: arquitectura de vectores tipados
Para lograr una máxima flexibilidad y eficiencia, los vectores JavaScript tipados separan la impementación en buffers y vistas. Un buffer (implementedo por el objeto ArrayBuffer
) es un objeto representando un segmento de datos; no tiene formato de referencia, y no ofrece mecanismo para acceso a su contenido. Para acceder a la memoria contenida en un buffer, necesitas usar una vista. Una vista provee un contexto — que es, un tipo de dato, despalzamiento de inicio, y numero de elementos — que vuelven los datos en un vector tipado real.
ArrayBuffer
El ArrayBuffer
es un tipo de datos que es usado para representar un buffer de datos binario de largo-fijado genérico. No puedes manipular directamente el contenido de un ArrayBuffer
; en su lugar, creas una vista de vector tipado o una DataView
que representa al buffer en un formato específico, y usas eso para leer y escribir los contenidos del buffer.
Vistas de vector tipado
Las vistas de vector tipado tienen nombres autodescriptivos y proveen vistas para todos los tipos numéricos usuales como Int8
, Uint32
, Float64,
etcétera. Hay una vista de vector tipado especial, el Uint8ClampedArray
. Restringe los valores entre 0 y 255. Esto es útil para procesamiento de datos de Canvas, por ejemplo.
Type | Size in bytes | Description | Web IDL type | Equivalent C type |
Int8Array |
1 | 8-bit two's complement signed integer | byte |
int8_t |
Uint8Array |
1 | 8-bit unsigned integer | octet |
uint8_t |
Uint8ClampedArray |
1 | 8-bit unsigned integer (clamped) | octet |
uint8_t |
Int16Array |
2 | 16-bit two's complement signed integer | short |
int16_t |
Uint16Array |
2 | 16-bit unsigned integer | unsigned short |
uint16_t |
Int32Array |
4 | 32-bit two's complement signed integer | long |
int32_t |
Uint32Array |
4 | 32-bit unsigned integer | unsigned long |
uint32_t |
Float32Array |
4 | 32-bit IEEE floating point number | unrestricted float |
float |
Float64Array |
8 | 64-bit IEEE floating point number | unrestricted double |
double |
DataView
La DataView
es una interface de bajo nivel que provee una API getter/setter para leer y escribir datos en el buffer arbitrariamente. Esto es útil cuando hay que lidiar con diferentes tipos de datos, por ejemplo. Las vistas de vector tipado están en el orden de bytes nativo (ver Endianness) de tu plataforma. Con una DataView
puedes controlar el orden de bytes. Es big-endian por defecto y puede ser cambiado a little-endian en los métodos getter/setter.
APIs de Web que usan vectores tipados
FileReader.prototype.readAsArrayBuffer()
- El método
FileReader.prototype.readAsArrayBuffer()
comienza leyendo los contenidos delBlob
oFile
especificado. XMLHttpRequest.prototype.send()
- El metodo
send()
de las instanciasXMLHttpRequest
ahora admite vectores tipados y objetosArrayBuffer
como argumento. ImageData.data
- Es un
Uint8ClampedArray
representando una matriz unidimensional conteniendo los datos en el orden RGBA, con valores enteros entre0
y255
inclusive.
Ejemplos
Usando vistas con buffers
Primero que todo, necesitaremos crear un buffer, aquí con un largo fijo de 16-bytes:
var buffer = new ArrayBuffer(16);
En este punto, tenemos un segmento de memoria cuyos bytes están todos preinicializados a 0. No hay mucho que podamos hacer con ello, sin embargo. Podemos confirmar que de hecho es de 16 bytes de largo, y de eso se trata:
if (buffer.byteLength === 16) { console.log("Sí, son 16 bytes."); } else { console.log("Oh no, es el tamaño equivocado!"); }
Antes que realmente podamos trabajar con este buffer, necesitamos crear una vista. Vamos a crear una vista que trate los datos en el buffer como un vector de enteros con signo de 32-bit:
var int32View = new Int32Array(buffer);
Ahora podemos acceder los campos en el vector como si fuera un vector normal:
for (var i = 0; i < int32View.length; i++) { int32View[i] = i * 2; }
Esto llena las 4 entradas en el vector (4 entradas a 4 bytes cada una hacen 16 bytes en total) con los valores 0, 2, 4, y 6.
Multiples vistas en los mismos datos
Las cosas comienzan a ponerse realmente interesante cuando consideras que puedes crear multiples vistas sobre los mismos datos. Por ejemplo, dado el código de arriba, podemos continuar como sigue:
var int16View = new Int16Array(buffer); for (var i = 0; i < int16View.length; i++) { console.log("Entrada " + i + ": " + int16View[i]); }
Aquí creamos una vista de entero de 16-bit que comparte el mismo buffer que la vista existente de 32-bit y mostramos todos los valores en el buffer como enteros de 16-bit. Ahora obtenemos la salida 0, 0, 2, 0, 4, 0, 6, 0.
Puedes ir un paso más lejos, sin embargo. Considera esto:
int16View[0] = 32; console.log("Entrada 0 en el vector de 32-bit es ahora " + int32View[0]);
La salida de esto es "Entrada 0 en el vector de 32-bit es ahora 32". En otras palabras, los dos vectores son de hecho simplemente vistas sobre el mismo buffer de datos, tratándolo como formatos diferentes. Puedes hacer esto con cualquier tipo de vista.
Trabajando con estructuras de datos complejas
Combinando un solo buffer con múltiples vistas de diferentes tipos, comenzando a diferentes desplazamientes dentro del buffer, puedes interactuar con objetos de datos conteniendo múltiples tipos de datos. Esto te permite, por ejemplo, interactuar con estructuras de datos complejas de WebGL, archivos de datos, o estructuras C que necesitas usar mientras se usa js-ctypes.
Considera esta estructura C:
struct someStruct { unsigned long id; char username[16]; float montoAdeudado; };
Puedes acceder un buffer conteniendo datos en este formato como sigue:
var buffer = new ArrayBuffer(24); // ... leer los datos en el buffer ... var idView = new Uint32Array(buffer, 0, 1); var usernameView = new Uint8Array(buffer, 4, 16); var montoAdeudadoView = new Float32Array(buffer, 20, 1);
Entonces puedes acceder, por ejemplo, el monto adeudado con montoAdeudadoView[0]
.
Conversión a vectores normales
Después de procesar un vector tipado, algunas veces es conveniente convertirlo de vuelta a un vector normal para beneficiarse del prototype de Array
. Esto puede hacerse usando Array.from
, o usando el siguiente código donde Array.from
no sea admitido.
var typedArray = new Uint8Array([1, 2, 3, 4]), normalArray = Array.prototype.slice.call(typedArray); normalArray.length === 4; normalArray.constructor === Array;
Especificaciones
Especificación | Estado | Comentario |
---|---|---|
Typed Array Specification | Obsoleta | Sustituido por ECMAScript 6. |
ECMAScript 2015 (6th Edition, ECMA-262) The definition of 'TypedArray Objects' in that specification. |
Standard |
Definición inicial en un estándar ECMA. |
Compatibilidad de navegador
Chrome 7 incluye soporte para ArrayBuffer
, Float32Array
, Int16Array
, y Uint8Array
. Chrome 9 y Firefox 15 añaden soporte para objetos DataView
. Internet Explorer 10 soporta todos los tipos excepto Uint8ClampedArray
y ArrayBuffer.prototype.slice
que están presentes comenzando en Internet Explorer 11.
Funcionalidad | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|
Soporte básico | 7.0 | 4.0 (2) | 10 | 11.6 | 5.1 |
Funcionalidad | Android | Chrome for Android | Firefox Mobile (Gecko) | IE Mobile | Opera Mobile | Safari Mobile |
---|---|---|---|---|---|---|
Soporte básico | 4.0 | (Yes) | 4.0 (2) | 10 | 11.6 | 4.2 |