Кратко
СкопированоData
— это специальный объект для обращения к нетипизированному представлению данных из Array
. Он содержит уникальные методы, позволяющие обратиться к данным, задавая их тип в момент обращения с возможностью явно указать порядок байтов.
Поскольку Data
предоставляет низкоуровневое API для записи и чтения данных из Array
, это делает его очень гибким инструментом для работы с бинарными форматами данных — изображениями, аудио и видеофайлами, сетевыми запросами. Если требуется взаимодействовать с буфером, где хранятся данные разного типа, то Data
просто необходим.
Пример
Скопировано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]);
buffer
— ссылка на бинарные данныеArray
;Buffer byte
— стартовая позиция данных для представления в байтах (смещение), по умолчанию - 0;Offset byte
— количество читаемых данных (в байтах) изLength buffer
, по умолчанию - до конца буфера.
Мы можем просто создать представление Data
для всего буфера:
// Создание буфера указанного размераconst buffer = new ArrayBuffer(16);// Создание представления DataViewconst dataView = new DataView(buffer);
// Создание буфера указанного размера const buffer = new ArrayBuffer(16); // Создание представления DataView const dataView = new DataView(buffer);
Или создать представление для его части, указав в аргументах byte
и byte
:
const buffer = new ArrayBuffer(16);// Создание представления, в котором со смещением на 2 считываем 4 байтаconst dataView = new DataView(buffer, 2, 4);
const buffer = new ArrayBuffer(16); // Создание представления, в котором со смещением на 2 считываем 4 байта const dataView = new DataView(buffer, 2, 4);
Свойства
СкопированоСвойства Data
аналогичны аргументам его конструктора:
buffer
— ссылка наArray
на который ссылается представление. Только для чтения;Buffer byte
— размер представления в байтах. Только для чтения;Length byte
— смещение представленияOffset Data
в байтах от начального значения вView Array
. Только для чтения.Buffer
Методы
СкопированоИмена методов Data
удобно связаны с числовыми типами (uint8
, float64
, ...), при этом методы начинающиеся с get
читают данные из буфера, а начинающиеся с set
их записывают. Например: .get
, .get
, .set
, .set
и т. д.
// Создаем бинарный массив размером 4 байтаconst buffer = new Uint8Array([0, 42, 0, 42]).buffer;const dataView = new DataView(buffer);// Получим 8-битное число на позиции 0console.log(dataView.getUint8(0));// 0// Теперь на той же позиции 0, получим 16-битное число, но оно уже состоит из двух байтconsole.log(dataView.getUint16(0));// 42// Теперь на той же позиции 0, получим 32-битное число, но оно уже состоит из четырех байтconsole.log(dataView.getUint32(0));// 2752554// Запишем нули на позицию 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 // Теперь на той же позиции 0, получим 16-битное число, но оно уже состоит из двух байт console.log(dataView.getUint16(0)); // 42 // Теперь на той же позиции 0, получим 32-битное число, но оно уже состоит из четырех байт console.log(dataView.getUint32(0)); // 2752554 // Запишем нули на позицию 0 для 16-битного числа dataView.setUint16(0, 0); // Теперь 32-битное число изменилось console.log(dataView.getUint32(0)); // 42
Указание порядка байтов
СкопированоПорядок байтов (endianness) — последовательность байтов, в которой информация хранится в памяти компьютера. По умолчанию в Data
используется порядок от старшего к младшему (big-endian). Про память подробнее вы можете узнать из статьи «Как устроена память».
Data
дает возможность явно указывать порядок байтов в момент чтения или записи данных. В обычных Typed
такой возможности нет. Возможность указания порядка байтов особенна важна когда порядок байтов в данных отличается от используемого в операционной системе. Подробнее о порядке байтов.
const buffer = new ArrayBuffer(4);const dataView = new DataView(buffer);// Запишем число 123 с явным указанием big-endian порядка байтов, для этого укажем falsedataView.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); new DataView(buffer).setInt16(0, 256, true); return new Int16Array(buffer)[0] === 256;})();console.log(littleEndian);// true или false
const littleEndian = (() => { const buffer = new ArrayBuffer(2); new DataView(buffer).setInt16(0, 256, true); return new Int16Array(buffer)[0] === 256; })(); console.log(littleEndian); // true или false
Обратите внимание, по умолчанию Data
использует именно big-endian, однако многие платформы используют little-endian.
Выход за границы буфера
Скопировано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 см. секции GetViewValue и SetViewValue) — например, проверки выхода за границы буфера, проверка значения endianness и т. п.
Разница в скорости не будет заметна для большинства единичных задач. Однако в высокопроизводительных сценариях, например, при обработке каждого пикселя в изображении использование Typed
с помощью uint8array
или uint8array
будет ощутимо быстрее, чем с обработка с применением методов Data
.