Los ArrayBuffers se usan para transportar datos sin procesar, y varias APIs nuevas dependen de ellos, como WebSockets, Web Intents 2](https://www.html5rocks.com/en/tutorials/file/xhr2/) y WebWorkers. Sin embargo, como llegaron recientemente al mundo de JavaScript, a veces se malinterpretan o se usan de forma inadecuada.
Semánticamente, un ArrayBuffer es simplemente un array de bytes que se ve a través de una máscara específica.
Esta máscara, una instancia de ArrayBufferView, define cómo se alinean los bytes para que coincidan con la estructura esperada del contenido. Por ejemplo, si sabes que los bytes de un ArrayBuffer representan un array de números enteros sin firmar de 16 bits, solo une el ArrayBuffer en una vista Uint16Array
y puedes manipular sus elementos con la sintaxis de corchetes como si Uint16Array
fuera un array de números enteros:
// suppose buf contains the bytes [0x02, 0x01, 0x03, 0x07]
// notice the multibyte values respect the hardware endianess, which is little-endian in x86
var bufView = new Uint16Array(buf);
if (bufView[0]===258) { // 258 === 0x0102
console.log("ok");
}
bufView[0] = 255; // buf now contains the bytes [0xFF, 0x00, 0x03, 0x07]
bufView[0] = 0xff05; // buf now contains the bytes [0x05, 0xFF, 0x03, 0x07]
bufView[1] = 0x0210; // buf now contains the bytes [0x05, 0xFF, 0x10, 0x02]
Una pregunta práctica común sobre ArrayBuffer es cómo convertir un String
en un ArrayBuffer
y viceversa. Dado que un ArrayBuffer es, de hecho, un array de bytes, esta conversión requiere que ambos extremos estén de acuerdo en cómo representar los caracteres en la cadena como bytes. Es probable que ya hayas visto este “acuerdo”: es la codificación de caracteres de la cadena (y las “condiciones del acuerdo” habituales son, por ejemplo, Unicode UTF-16 y iso8859-1). Por lo tanto, si tú y la otra parte
acordaron la codificación UTF-16, el código de conversión podría ser algo como lo siguiente:
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint16Array(buf));
}
function str2ab(str) {
var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
var bufView = new Uint16Array(buf);
for (var i=0, strLen=str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
Observa el uso de Uint16Array
. Esta es una vista de ArrayBuffer que alinea los bytes de los ArrayBuffers como elementos de 16 bits. No controla la codificación de caracteres, que String.fromCharCode
y str.charCodeAt
manejan como Unicode.
Una pregunta popular sobre esto en StackOverflow tiene una respuesta con muchos votos y una solución algo complicada para la conversión: crea un FileReader
para que actúe como convertidor y alimenta un Blob
que contenga la cadena. Si bien este método funciona, tiene una baja legibilidad y creo que es lento. Dado que las sospechas infundadas han provocado muchos errores en la historia de la humanidad, adoptemos un enfoque más científico. Realicé pruebas de rendimiento con JSPerf en los dos métodos, y el resultado confirma mi sospecha. Consulta la demostración aquí.
En Chrome 20, es casi 27 veces más rápido usar el código de manipulación directa de ArrayBuffer
de este artículo que usar el método FileReader
/Blob
.