Кратко
СкопированоData помогает работать с содержимым Array. С его помощью можно читать и записывать данные разных типов, а также указывать порядок интерпретации байтов.
Пример
Скопировано
const buffer = new ArrayBuffer(4)const dataView = new DataView(buffer)dataView.setUint8(0, 255)dataView.setUint8(1, 255)console.log(dataView.getUint8(0))// 255console.log(dataView.getUint16(0))// 65535
const buffer = new ArrayBuffer(4)
const dataView = new DataView(buffer)
dataView.setUint8(0, 255)
dataView.setUint8(1, 255)
console.log(dataView.getUint8(0))
// 255
console.log(dataView.getUint16(0))
// 65535
Как пишется
СкопированоСоздать Data можно с помощью оператора new:
new DataView(buffer, [byteOffset], [byteLength])
new DataView(buffer, [byteOffset], [byteLength])
Data при создании принимает следующие аргументы:
buffer— объектArray;Buffer byte— смещение по даннымOffset Array(в байтах), с учётом которого будет создаваться представлениеBuffer Data. По умолчанию — 0;View byte— количество байтов, доступных в создаваемом представлении. По умолчанию — все данные до конца буфера.Length
Создание представления
СкопированоМожно создать Data для всего буфера:
// Создаём буфер размером 16 байтconst buffer = new ArrayBuffer(16)// Создаём представление для всего буфераconst dataView = new DataView(buffer)
// Создаём буфер размером 16 байт
const buffer = new ArrayBuffer(16)
// Создаём представление для всего буфера
const dataView = new DataView(buffer)
Или для части буфера, указав смещение и длину:
const buffer = new ArrayBuffer(16)// Создаём представление, которое начинается// со смещением на 2 байта от начала buffer и занимает 4 байтаconst dataView = new DataView(buffer, 2, 4)
const buffer = new ArrayBuffer(16)
// Создаём представление, которое начинается
// со смещением на 2 байта от начала buffer и занимает 4 байта
const dataView = new DataView(buffer, 2, 4)
Свойства
СкопированоУ Data есть три основных свойства:
buffer— ссылка на исходныйArray;Buffer byte— смещение представления от начала буфера в байтах;Offset byte— размер представления в байтах.Length
Методы
СкопированоМетоды Data начинаются с get (чтение) или set (запись), а дальше указывается тип данных: Uint8, Int16, Float32 и так далее.
Рассмотрим на примере:
// Создаем бинарный массив размером 4 байтаconst buffer = new Uint8Array([0, 42, 0, 42]).bufferconst dataView = new DataView(buffer)// Читаем 8-битное беззнаковое число с позиции 0console.log(dataView.getUint8(0))// 0// Читаем 16-битное беззнаковое число с позиции 0 (оно состоит из двух байт)console.log(dataView.getUint16(0))// 42// Читаем 32-битное беззнаковое число с позиции 0 (оно состоит из четырех байт)console.log(dataView.getUint32(0))// 2752554// Записываем 0 на позицию 0 для 16-битного беззнакового числаdataView.setUint16(0, 0)// Теперь 32-битное число изменилосьconsole.log(dataView.getUint32(0))// 42
// Создаем бинарный массив размером 4 байта
const buffer = new Uint8Array([0, 42, 0, 42]).buffer
const dataView = new DataView(buffer)
// Читаем 8-битное беззнаковое число с позиции 0
console.log(dataView.getUint8(0))
// 0
// Читаем 16-битное беззнаковое число с позиции 0 (оно состоит из двух байт)
console.log(dataView.getUint16(0))
// 42
// Читаем 32-битное беззнаковое число с позиции 0 (оно состоит из четырех байт)
console.log(dataView.getUint32(0))
// 2752554
// Записываем 0 на позицию 0 для 16-битного беззнакового числа
dataView.setUint16(0, 0)
// Теперь 32-битное число изменилось
console.log(dataView.getUint32(0))
// 42
Посмотреть список всех методов Data можно в спецификации.
Указание порядка байтов
СкопированоПорядок байтов (endianness) — это правило, определяющее, в каком порядке байты интерпретируются при формировании числа. Например, для двух байтов [0x01 при порядке от старшего к младшему (big-endian) получится число 0x0102 (258), а при порядке от младшего к старшему (little-endian) — 0x0201 (513). По умолчанию Data использует big-endian. Подробнее о памяти можно узнать из статьи «Как устроена память».
Data позволяет явно указать порядок интерпретации байтов т. к. принимает флаг little-endian при чтении или записи. В обычных типизированных массивах такой возможности нет. Это особенно важно, когда вы читаете данные из внешнего источника (например, из файла или по сети), который использует порядок интерпретации байтов, отличный от порядка, принятого в вашей операционной системе. Например, в сетевых протоколах часто используется big-endian, а в операционных системах персональных компьютеров — little-endian и если не указать правильный порядок, числа будут прочитаны неверно. Data позволяет легко подстроиться под нужный формат.
const buffer = new ArrayBuffer(4)const dataView = new DataView(buffer)// Записываем беззнаковое число 123 с указанием порядка big-endian (false)dataView.setUint16(0, 123, false)// Читаем беззнаковое число с указанием порядка big-endianconsole.log(dataView.getUint16(0, false))// 123 - все верно// Читаем беззнаковое число, как будто оно записано в порядке little-endianconsole.log(dataView.getUint16(0, true))// 31488 - получили другое число!
const buffer = new ArrayBuffer(4)
const dataView = new DataView(buffer)
// Записываем беззнаковое число 123 с указанием порядка big-endian (false)
dataView.setUint16(0, 123, false)
// Читаем беззнаковое число с указанием порядка big-endian
console.log(dataView.getUint16(0, false))
// 123 - все верно
// Читаем беззнаковое число, как будто оно записано в порядке little-endian
console.log(dataView.getUint16(0, true))
// 31488 - получили другое число!
Порядок байтов в используемом окружении
Вот пример, как с помощью Data можно узнать порядок интерпретации байтов в используемом окружении:
const littleEndian = (() => { const buffer = new ArrayBuffer(2) // Указываем, что данные записываются в формате little-endian (true) для знакового 16-битного числа new DataView(buffer).setInt16(0, 256, true) // Типизированные массивы используют порядок интерпритации байтов платформы return new Int16Array(buffer)[0] === 256})()// Вернет true для little-endian или false для big-endianconsole.log(littleEndian)
const littleEndian = (() => {
const buffer = new ArrayBuffer(2)
// Указываем, что данные записываются в формате little-endian (true) для знакового 16-битного числа
new DataView(buffer).setInt16(0, 256, true)
// Типизированные массивы используют порядок интерпритации байтов платформы
return new Int16Array(buffer)[0] === 256
})()
// Вернет true для little-endian или false для big-endian
console.log(littleEndian)
По умолчанию Data использует big-endian, но многие платформы работают и с little-endian.
Например, little-endian используется в x86 процессорах (Intel, AMD). Архитектура ARM тоже чаще использует little-endian.
При этом популярные сетевые протоколы (TCP/IP, UDP) и форматы файлов (JPEG, PNG) используют big-endian. Поэтому важно контролировать порядок байтов.
Как понять
СкопированоData — это низкоуровневый инструмент. Он пригодится, когда нужно работать с бинарными данными: изображениями, аудио, видео или сетевыми запросами.
Его особенность — возможность указать порядок интерпретации байтов при чтении и записи. Это позволяет работать с данными, у которых порядок интерпретации байтов отличается от порядка интерпретации байтов в системе. А ещё Data полезен, если в буфере хранятся данные разного типа (например, сами данные и служебная информация к ним).
Выход за границы буфера
СкопированоData сам следит за тем, чтобы вы не вышли за границы буфера. Если попытаться прочитать данные за пределами Array, возникнет ошибка Range.
const buffer = new ArrayBuffer(2)const dataView = new DataView(buffer)console.log(dataView.getUint32(0))// Ошибка: Uncaught RangeError: Offset is outside the bounds of the DataViewconsole.log(dataView.getUint16(1))// Ошибка: Uncaught RangeError: Offset is outside the bounds of the DataView
const buffer = new ArrayBuffer(2)
const dataView = new DataView(buffer)
console.log(dataView.getUint32(0))
// Ошибка: Uncaught RangeError: Offset is outside the bounds of the DataView
console.log(dataView.getUint16(1))
// Ошибка: Uncaught RangeError: Offset is outside the bounds of the DataView
На практике
Скопированосоветует
СкопированоData даёт гибкость и контроль, но это достигается ценой производительности. При каждом чтении или записи Data выполняет дополнительные проверки, (согласно спецификации ECMAScript) — например, проверяет границы буфера и порядок байтов.
Для большинства задач разница в скорости незаметна. Но в высокопроизводительных сценариях (например, обработка каждого пикселя изображения) использование типизированных массивов через uint8array или uint8array сработает значительно быстрее, чем методы Data.