W3docs

JavaScript ArrayBuffer, двоичные массивы

JavaScript предоставляет ArrayBuffer и типизированные массивы для работы с двоичными данными: файлами, изображениями, аудио и WebSocket-ответами.

В большинстве случаев JavaScript работает с текстом и JSON, но как только вы начинаете работать с файлами, изображениями, аудио, WebSocket-ами или ответами fetch, вы имеете дело с сырыми байтами. JavaScript обрабатывает эти байты через ArrayBuffer и семейство представлений типизированных массивов. В этой главе объясняется, что представляет собой каждый элемент, как они связаны между собой и когда их использовать — с запускаемыми примерами, которые вы можете проверить самостоятельно.

Мысленная модель состоит из двух уровней:

  • ArrayBuffer — блок сырой памяти фиксированной длины. Это просто байты; читать и записывать их напрямую невозможно.
  • Представления (типизированные массивы и DataView) — объекты, которые интерпретируют эти байты как числа. Чтение и запись всегда выполняются через представление.

Что такое ArrayBuffer?

ArrayBuffer — это универсальный контейнер сырых двоичных данных фиксированной длины: блок памяти, измеряемый в байтах. У него нет понятия «элементов» или типов; он знает только, сколько байт содержит. Размер задаётся при создании и не может быть изменён.

javascript— editable

Чтобы что-то сделать с этими байтами, нужно обернуть буфер в представление.

Типизированные массивы: представления над буфером

Типизированный массив — это представление, которое интерпретирует буфер как массив чисел фиксированного типа. Индексирование, итерация и арифметика работают так же, как в обычном array, но данные хранятся в сырой памяти буфера, что делает типизированные массивы компактными и быстрыми для больших числовых наборов данных.

Тип представления определяет, сколько байт занимает каждый элемент:

ПредставлениеБайт на элементДиапазон значений
Uint8Array10 … 255
Int8Array1-128 … 127
Uint16Array20 … 65535
Int16Array2-32768 … 32767
Uint32Array40 … 4294967295
Int32Array4-2³¹ … 2³¹-1
Float32Array432-битное число с плавающей точкой
Float64Array864-битное число с плавающей точкой
Uint8ClampedArray10 … 255 (с ограничением)

Создание типизированных массивов

Типизированный массив можно создать из длины, из существующего array или поверх существующего буфера.

javascript— editable

Несколько представлений могут использовать один буфер

Это ключевая идея: несколько представлений могут находиться поверх одного и того же буфера. Запись через одно представление изменяет байты, и все остальные представления немедленно видят это изменение. Именно так можно интерпретировать одни и те же байты разными способами.

javascript— editable

Переполнение и ограничение

Каждый элемент имеет фиксированную ширину, поэтому значения вне диапазона оборачиваются (берётся остаток от деления на размер типа). Uint8ClampedArray — исключение: он ограничивает значения диапазоном 0…255, что именно то, что нужно для пиксельных данных canvas.

javascript— editable

Срезы и под-представления

slice() копирует данные в новый буфер, тогда как subarray() возвращает другое представление поверх той же памяти — дешевле, но записи разделяются.

javascript— editable

DataView: смешанные типы и порядок байт

Типизированный массив навязывает один тип и использует нативный порядок байт платформы. DataView — низкоуровневая альтернатива: он позволяет читать и записывать разные типы по любому смещению байт и позволяет явно выбирать порядок байт (endianness). Этот контроль необходим при разборе форматов файлов и сетевых протоколов, где порядок байт фиксируется спецификацией, а не вашим процессором.

Endianness — это порядок, в котором хранится многобайтовое число: big-endian ставит старший байт первым, little-endian — последним. Каждый метод get/set у DataView принимает флаг littleEndian (по умолчанию используется big-endian).

javascript— editable

Вот то же 32-битное число, записанное в обоих порядках байт, чтобы вы могли увидеть разницу:

javascript— editable

Преобразование между байтами и другими типами

Двоичные данные редко существуют сами по себе — вы постоянно перемещаетесь между буферами, строками и объектами более высокого уровня.

Байты ↔ текст

Чтобы преобразовать string в байты (и обратно), используйте TextEncoder / TextDecoder, которые кодируют в UTF-8. Смотрите TextDecoder и TextEncoder для полного описания API.

javascript— editable

Доступ к базовому буферу

Каждый типизированный массив предоставляет свой buffer, а также byteOffset и byteLength, описывающие охватываемый им регион. Именно так передаются байты в API, которому нужен ArrayBuffer.

javascript— editable

Где реально используются двоичные массивы

  • Файлы. Чтение файла как ArrayBuffer позволяет проверять заголовки, разбирать форматы или загружать сырые байты. Смотрите Blob и File API.
  • Сеть. fetch(...).arrayBuffer() и двоичные кадры WebSocket дают вам ArrayBuffer для декодирования.
  • Canvas. ctx.getImageData().data — это Uint8ClampedArray байтов пикселей RGBA, которые можно читать и перезаписывать.
  • WebGL / Web Audio. Вершинные буферы и аудиосэмплы представляют собой типизированные массивы для обеспечения производительности.

Пример: чтение файла как ArrayBuffer

В браузере FileReader (или более новый Blob.arrayBuffer()) считывает содержимое файла в буфер, который затем разбирается с помощью DataView или типизированного массива. Более подробное описание смотрите в File API.

function readBinaryFile(file) {
  const reader = new FileReader();
  reader.onload = () => {
    const view = new DataView(reader.result);
    console.log(view.getUint8(0)); // first byte, e.g. a format signature
  };
  reader.readAsArrayBuffer(file);
}

// Modern alternative:
// const buffer = await file.arrayBuffer();

Рекомендации

  • Выбирайте наименьший подходящий тип. Uint8Array для потоков байт, Float64Array для точных вычислений — использование более широкого типа, чем нужно, расходует память впустую.
  • Используйте DataView только когда нужны смешанные типы или фиксированный порядок байт. Для однородного числового array типизированный массив проще и быстрее.
  • Переиспользуйте буферы в горячих циклах, чтобы снизить нагрузку на выделение памяти и сборщик мусора.
  • Помните, что представления разделяют память — используйте slice(), когда нужна независимая копия, и subarray(), когда намеренно хотите создать псевдоним.

Итоги

  • ArrayBuffer — это сырая память фиксированной длины; вы никогда не обращаетесь к ней напрямую.
  • Типизированные массивы рассматривают эту память как числа одного фиксированного типа и ведут себя как array.
  • DataView читает и записывает смешанные типы по произвольным смещениям с явным указанием порядка байт — идеально подходит для разбора протоколов и форматов файлов.
  • Представления поверх одного буфера разделяют память, поэтому запись через одно видна через все остальные.
  • Эти типы лежат в основе работы с файлами, сетевыми ответами, пикселями canvas, аудио и WebGL.

Практика

Практика
What are the uses and properties of ArrayBuffer and Binary Arrays in JavaScript as per the URL content?
What are the uses and properties of ArrayBuffer and Binary Arrays in JavaScript as per the URL content?
Was this page helpful?