Skip to content

ArrayBuffer、TypedArray 与 DataView 详解


ArrayBuffer - 原始二进制数据缓冲区

[补充说明]:ArrayBuffer 表示通用的、固定长度的原始二进制数据缓冲区。

javascript
// 创建 ArrayBuffer [修正术语:Arraybuffer → ArrayBuffer]
const buffer = new ArrayBuffer(16); // 创建16字节的缓冲区
console.log(buffer.byteLength);    // 16

// 不能直接操作 ArrayBuffer,需要通过视图操作
// console.log(buffer[0]); // 错误:不能直接访问
TypedArray - 类型化数组视图

[补充说明]:TypedArray 提供了对 ArrayBuffer 的类型化视图访问。 在 JavaScript 中,TypedArray(类型化数组) 是一组用于处理二进制数据的数组-like对象,专为高效操作原始二进制数据而设计。它们与普通数组(Array)的主要区别在于:存储的数据类型固定(如整数、浮点数等),且长度不可变,因此性能更高,尤其适合处理大量二进制数据(如音频、图像、网络协议数据等)。

TypedArray 的特点
  1. 固定数据类型:每个元素的类型在创建时确定(如 8位整数、32位浮点数等),避免了普通数组的动态类型转换开销。
  2. 固定长度:创建时需指定长度,创建后无法通过 push/pop 等方法改变(类似 C 语言的数组)。
  3. 存储原始二进制数据:直接映射到内存中的二进制数据缓冲区(ArrayBuffer),读写效率远高于普通数组。
  4. 不继承 Array.prototype:不支持 Array 的部分方法(如 concatsplice),但支持索引访问和 length 属性。
常见的 TypedArray 类型

JavaScript 提供了多种 TypedArray 构造函数,对应不同的数据类型和字节长度:

构造函数元素类型字节长度取值范围(示例)
Int8Array8位有符号整数1-128 到 127
Uint8Array8位无符号整数10 到 255
Uint8ClampedArray8位无符号整数(截断)1超出范围时自动截断为 0 或 255
Int16Array16位有符号整数2-32768 到 32767
Uint16Array16位无符号整数20 到 65535
Int32Array32位有符号整数4-2³¹ 到 2³¹-1
Uint32Array32位无符号整数40 到 2³²-1
Float32Array32位浮点数(单精度)4约 ±3.4×10³⁸
Float64Array64位浮点数(双精度)8约 ±1.8×10³⁰⁸

TypedArray 的使用示例 TypedArray 必须基于 ArrayBuffer(原始二进制数据缓冲区)创建,直接创建会在内部自动创建,ArrayBuffer 是一块原始的内存区域,TypedArray 则是对这块内存的“视图”(按指定类型解析数据)。

1. 基本创建与使用
javascript
// 1. 创建一个 16 字节的 ArrayBuffer(内存缓冲区)
const buffer = new ArrayBuffer(16);

// 2. 创建 TypedArray 视图(以 32位整数解析 buffer)
const int32View = new Int32Array(buffer);

// 3. 长度由缓冲区大小和元素字节长度决定:16字节 / 4字节/元素 = 4个元素
console.log(int32View.length); // 4

// 4. 操作元素(索引访问)
int32View[0] = 100;
int32View[1] = 200;
console.log(int32View); // Int32Array(4) [100, 200, 0, 0]
// 查看对应的原始二进制数据缓冲区
// ArrayBuffer {
//   [Uint8Contents]: <64 00 00 00 c8 00 00 00 00 00 00 00 00 00 00 00>,
//   byteLength: 16
// }

// 5. 不同视图解析同一块缓冲区(二进制数据的灵活解读)
const uint8View = new Uint8Array(buffer);
console.log(uint8View); // Uint8Array(16) [100, 0, 0, 0, 200, 0, 0, 0, 0, ...]

64 00 00 00 c8 00 00 00 00 00 00 00 00 00 00 00 低位—————————————————高位 对应16个字节,int32是用32个位,就是4个字节,也就是四个一组,转换为10进制: 对应100,200,0,0

2. 直接初始化

也可以直接通过长度或数组初始化 TypedArray(内部会对应的==自动创建== ==ArrayBuffer==):

javascript
// 用长度初始化(8个 16位整数)
const int16Arr = new Int16Array(8)
int16Arr[0] = 32767 // 最大16位有符号整数
console.log(int16Arr.buffer);
// ArrayBuffer {
//   [Uint8Contents]: <ff 7f 00 00 00 00 00 00 00 00 00 00 00 00 00 00>,
//   byteLength: 16
// }

// 用数组初始化
const float64Arr = new Float64Array([1.5, 2.5, 3.5])
console.log(float64Arr[1]) // 2.5
console.log(float64Arr.buffer)
// ArrayBuffer {
//   [Uint8Contents]: <00 00 00 00 00 00 f8 3f 00 00 00 00 00 00 04 40 00 00 00 00 00 00 0c 40>,
//   byteLength: 24
// }
TypedArray 与普通数组的核心区别
特性TypedArray普通数组(Array
元素类型固定(如 Int32Float64动态(可混合各种类型)
长度固定(创建后不可变)可变(可通过 push 等方法修改)
存储方式二进制缓冲区(高效)动态对象存储(有额外开销)
继承关系不继承 Array.prototype继承 Array.prototype
适用场景二进制数据处理(音视频等)通用数据存储与操作
javascript
// 不同类型的 TypedArray 视图
const int8Array = new Int8Array(buffer);      // 8位有符号整数
const uint8Array = new Uint8Array(buffer);    // 8位无符号整数
const int16Array = new Int16Array(buffer);    // 16位有符号整数
const uint16Array = new Uint16Array(buffer);  // 16位无符号整数
const int32Array = new Int32Array(buffer);    // 32位有符号整数
const uint32Array = new Uint32Array(buffer);  // 32位无符号整数
const float32Array = new Float32Array(buffer); // 32位浮点数
const float64Array = new Float64Array(buffer); // 64位浮点数

// 直接创建带数据的 TypedArray
const dataArray = new Uint8Array([1, 2, 3, 4, 5]);
console.log(dataArray.length);    // 5
console.log(dataArray.byteLength); // 5
DataView - 灵活的数据访问视图

[补充说明]:DataView 提供了更灵活的低级接口来读写 ArrayBuffer 中的数据。

javascript
// 创建 DataView
const buffer = new ArrayBuffer(4)
const view = new DataView(buffer)


// 设置和读取数据(可以指定字节偏移和端序)
view.setInt8(0, 127) // 在位置0写入8位有符号整数
console.log(view.buffer)
// ArrayBuffer { [Uint8Contents]: <7f 00 00 00>, byteLength: 4 }

// 小端序写入16位无符号整数
view.setUint16(2, 65534, true) 
console.log(view.buffer);
// ArrayBuffer { [Uint8Contents]: <7f 00 fe ff>, byteLength: 4 }


console.log(view.getInt8(0))
// 127 因为就一位所以不分高低了

console.log(view.getUint16(2, true))
// 65534 (小端序读取)从低位(fe)到高位(ff)

console.log(view.getUint16(2))
// 65279 (大端序读取)反之
内存布局与字节序

[补充说明]:理解内存布局对于二进制数据处理至关重要。

javascript
// 演示字节序(Endianness)的影响
const testBuffer = new ArrayBuffer(4);
const view1 = new DataView(testBuffer);

// 写入32位整数
view1.setInt32(0, 0x12345678, false); // 大端序
console.log(new Uint8Array(testBuffer)); // [0x12, 0x34, 0x56, 0x78]

view1.setInt32(0, 0x12345678, true);  // 小端序
console.log(new Uint8Array(testBuffer)); // [0x78, 0x56, 0x34, 0x12]
==实际应用示例==
javascript
// 示例:处理二进制文件格式(如图像文件头)
function parsePNGHeader(buffer) {
    const view = new DataView(buffer);
    
    // 检查PNG文件签名
    const signature = view.getUint32(0, false);
    if (signature !== 0x89504E47) {
        throw new Error('不是有效的PNG文件');
    }
    
    // 读取图像尺寸
    const width = view.getUint32(16, false);
    const height = view.getUint32(20, false);
    
    return { width, height };
}

// 示例:创建和操作二进制数据
function createBinaryData() {
    const buffer = new ArrayBuffer(12);
    const view = new DataView(buffer);
    
    // 写入不同类型的数据
    view.setFloat32(0, Math.PI, true);    // 32位浮点数(小端序)
    view.setUint16(4, 1000, false);       // 16位无符号整数(大端序)
    view.setInt8(6, -50);                 // 8位有符号整数
    
    return buffer;
}
性能优化与使用场景
数据类型适用场景优点缺点
ArrayBuffer原始二进制数据存储内存高效不能直接操作
TypedArray类型化数据处理高性能,类型安全固定数据类型
DataView混合数据类型访问灵活,支持端序控制性能稍低
javascript
// 性能比较:TypedArray vs DataView
const testData = new ArrayBuffer(1000000);

// 使用 TypedArray(更快)
console.time('TypedArray');
const uint8View = new Uint8Array(testData);
for (let i = 0; i < uint8View.length; i++) {
    uint8View[i] = i % 256;
}
console.timeEnd('TypedArray');

// 使用 DataView(更灵活但稍慢)
console.time('DataView');
const dataView = new DataView(testData);
for (let i = 0; i < testData.byteLength; i++) {
    dataView.setUint8(i, i % 256);
}
console.timeEnd('DataView');
与 Blob 的转换
javascript
// ArrayBuffer 转 Blob
const arrayBuffer = new ArrayBuffer(1024);
const blob = new Blob([arrayBuffer], {type: 'application/octet-stream'});

// Blob 转 ArrayBuffer
async function blobToArrayBuffer(blob) {
    return await blob.arrayBuffer(); // 现代浏览器支持的方法
    // 或使用 FileReader
    // return new Promise((resolve) => {
    //     const reader = new FileReader();
    //     reader.onloadend = () => resolve(reader.result);
    //     reader.readAsArrayBuffer(blob);
    // });
}

Blob、File 与 Object URL 详解


Blob 对象

[补充说明]:Blob(Binary Large Object)表示不可变的原始数据类文件对象。

javascript
// 创建 Blob 对象
let debug = { name: 'Dano' }
let str = JSON.stringify(debug)
console.log(str)
// {"name":"Dano"} 15个字符

// 语法:new Blob(array, options)
let blob = new Blob([str], { type: 'application/json' })

// 获取 Blob 信息
console.log(blob) // Blob { size: 15, type: 'application/json' }
console.log(blob.size) // Blob 大小(字节)15
console.log(blob.type) // MIME 类型 application/json

在JavaScript中,字符的存储方式取决于字符串的编码类型。JavaScript字符串使用UTF-16编码,这意味着每个字符通常使用==2个字节==(16位)存储。但是,对于Unicode中超出基本多文种平面(BMP)的字符(即码点大于0xFFFF的字符),它们需要使用两个16位码元(即==4个字节==)来表示,这称为代理对(surrogate pair)。

因此,一般情况下,一个字符占用2个字节,但对于辅助平面中的字符,则占用4个字节。

JavaScript
// 创建 Blob 对象
let debug = { name: 'Dano😊' }
let str = JSON.stringify(debug)
console.log(str)

let blob = new Blob([str], { type: 'application/json' })

// 获取 Blob 信息
console.log(blob) 
console.log(blob.size) 
console.log(blob.type) 
// {"name":"Dano😊"}
// Blob { size: 19, type: 'application/json' }
// 19
// application/json
File 对象

[补充说明]:File 对象继承自 Blob,表示文件数据。

javascript
// 通过 input 获取 File 对象
const fileInput = document.getElementById('fileInput');
fileInput.addEventListener('change', (event) => {
    const file = event.target.files[0];
    console.log(file.name);    // 文件名
    console.log(file.size);    // 文件大小
    console.log(file.type);    // 文件类型
    console.log(file.lastModified); // 最后修改时间
});

// 创建 File 对象
const file = new File([JSON.stringify(debug)], 'user.json', {
    type: 'application/json',
    lastModified: Date.now()
});
FileReader 读取文件内容

[修正代码]:FileReader是浏览器独有的API在Node中

javascript
let json = { name: 'Dano😊' }

const file = new File([JSON.stringify(json)], 'user.json', {
    type: 'application/json',
    lastModified: Date.now(),
})

console.log(file)
// File {
//   size: 19,
//   type: 'application/json',
//   name: 'user.json',
//   lastModified: 1757595534532
// }

let reader = new FileReader()
reader.readAsArrayBuffer(file)

reader.onload = function (e) {
    const arrayBuffer = e.target.result
    // 将 ArrayBuffer 转换为字符串
    const decoder = new TextDecoder('utf-8')
    const jsonString = decoder.decode(arrayBuffer)
    const parsedJson = JSON.parse(jsonString)

    // 显示结果
    console.log(file, arrayBuffer, parsedJson)
}

reader.onerror = function (e) {
    console.error('读取文件时出错: ', e.target.error)
}

Pasted image 20250911211549.png

Object URL

[补充说明]:创建指向Blob/File对象的临时URL

javascript
function download() {
    let debug = {name: 'Domo'};
    let blob = new Blob([JSON.stringify(debug)], {type: 'application/json'});
    
    // 创建下载链接
    let a = document.createElement('a');
    a.download = 'user.json';
    a.rel = 'noopener'; // 安全优化
    a.href = URL.createObjectURL(blob);
    
    // 模拟点击下载
    a.click();
    
    // 释放URL资源 [补充说明]
    URL.revokeObjectURL(a.href);
}
Base64 与 DataURL

[补充说明]:

javascript
// Data URL 格式:data:[<mediatype>][;base64],<data>
const dataURL = 'data:application/json;base64,eyJuYW1lIjoiRG9tbyJ9';

// Base64 编码解码
const base64Encoded = btoa('Hello World'); // 编码
const base64Decoded = atob('SGVsbG8gV29ybGQ='); // 解码

// Blob 转 Base64
function blobToBase64(blob) {
    return new Promise((resolve) => {
        const reader = new FileReader();
        reader.onloadend = () => resolve(reader.result);
        reader.readAsDataURL(blob);
    });
}

Base64 和 Data URL 不是同一个东西。 它们的关系非常密切,但扮演着完全不同的角色。 简单来说:

  • Base64 是一种编码方式(把二进制数据变成文本)。
  • Data URL 是一种 URL 方案(一种特殊格式的网址)。
  • Data URL 经常使用 Base64 编码作为其数据部分
Data URL
  • 是什么:一种特殊格式的URL(统一资源定位符),它的协议头不是 http:https:,而是 data:。它允许将数据直接内嵌在文档中,而无需从外部服务器加载。
  • 结构:一个完整的Data URL有固定的格式,它包含了元数据数据本身data:[<mediatype>][;base64],<data>
  • data::协议头,表明这是一个Data URL。
  • [<mediatype>]:可选的MIME类型,告诉浏览器数据的格式是什么。例如 image/png, text/css, application/pdf。如果省略,默认为 text/plain
  • [;base64]:可选的标识符。如果存在,表示后面的 <data> 部分是经过 Base64编码的。如果不存在,则表示数据是URL编码的文本(通常仅适用于纯文本或SVG)。
  • ,:分隔符,前面是元信息,后面是数据本体。
  • <data>:实际的数据内容。如果使用了 ;base64,这里就是Base64编码后的字符串。

例子: 一个PNG图片的Data URL可能长这样: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==

关系
  • 依赖关系:Data URL 可以使用Base64编码,但不是必须的。对于二进制数据(如图片、PDF),必须使用Base64编码才能放入Data URL。对于纯文本(如CSS或SVG代码),可以不使用Base64,直接写入。
  • 协作方式:Data URL 提供了一个完整的“包装”,告诉浏览器“这里面是什么类型的数据”以及“数据是如何编码的”。而Base64编码则负责完成实际的数据转换工作,将二进制数据变成Data URL能够安全使用的文本形式。
内存管理最佳实践

[补充说明]:

  1. 及时释放 Object URL
javascript
const objectURL = URL.createObjectURL(blob);
// 使用完成后立即释放
URL.revokeObjectURL(objectURL);
  1. 大文件处理
javascript
// 使用切片处理大文件
const chunkSize = 1024 * 1024; // 1MB
let offset = 0;

while (offset < file.size) {
    const chunk = file.slice(offset, offset + chunkSize);
    // 处理分片...
    offset += chunkSize;
}
使用场景对比
技术适用场景优点缺点
Object URL临时显示或下载文件无需服务器,性能好需要手动释放内存
Data URL小文件内联自包含,无需额外请求体积增大33%,不适合大文件
FileReader读取文件内容多种读取方式异步操作,需要回调

MIME


MIME 类型(也称为媒体类型内容类型)是一种标准,用来表示文档、文件或字节流的性质和格式。 它的全称是 Multipurpose Internet Mail Extensions(多用途互联网邮件扩展)。顾名思义,它最初是为了在电子邮件中支持非 ASCII 字符和附件而设计的,但现在它被广泛应用在互联网的各个领域,尤其是 HTTP 协议中。

MIME 类型就是答案。 服务器在发送数据之前,会在 HTTP 响应的头部(Headers)中包含一个 Content-Type 字段,告诉浏览器(或其他客户端)正在发送的数据是什么类型。

Content-Type: text/html; charset=UTF-8 这行代码告诉浏览器:“接下来要发送的内容是 HTML 文本,使用的字符编码是 UTF-8”。浏览器收到这个信号后,就会启动 HTML 解析器来渲染页面。 如果没有 MIME 类型,浏览器只能靠猜测来处理数据,结果会非常不可靠和安全。

MIME 类型由类型(type) 和子类型(subtype) 两部分组成,中间由一条正斜杠 / 连接。 格式:type/subtype

1. 类型(Type)

代表大的类别,说明数据的一般类型。常见的顶级类型有:

  • text: 文本文件,人类可读的文字内容。

    • 例如:text/plaintext/htmltext/css
  • image: 图像文件或图形数据。

    • 例如:image/jpegimage/pngimage/gif
  • audio: 音频或音乐数据。

    • 例如:audio/mpegaudio/wav
  • video: 视频或动态图像数据。

    • 例如:video/mp4video/webm
  • application: 二进制数据或不属于其他类别的数据。通常是需要由应用程序处理的文件。

    • 例如:application/pdfapplication/jsonapplication/javascript
  • multipart: 由多个部分组成的数据,每个部分可以有自己独立的类型。常用于电子邮件或表单提交。

    • 例如:multipart/form-data
  • font: 字体文件。

    • 例如:font/woff2font/ttf
2. 子类型(Subtype)

代表特定类型中的具体格式。它精确地指定了是哪种 text 或哪种 image。 例如,同是 text 类型,text/plain 是纯文本,而 text/html 是 HTML 文档。

常见 MIME 类型示例
文件格式MIME 类型说明
.htmltext/htmlHTML 文档
.csstext/cssCSS 样式表
.jsapplication/javascriptJavaScript 文件
.jsonapplication/jsonJSON 数据
.pngimage/pngPNG 图像
.jpg / .jpegimage/jpegJPEG 图像
.pdfapplication/pdfAdobe PDF 文档
.zipapplication/zipZIP 压缩档案
.mp3audio/mpegMP3 音频
.mp4video/mp4MP4 视频
重要参数:charset

对于一些文本类型的文件,经常需要指定一个重要的参数——字符编码(character set)。这用 charset 来表示,与主类型用分号 ; 分隔。

  • 示例: text/html; charset=UTF-8
  • 作用: 告诉浏览器应该使用哪种字符编码来解读文本,防止出现乱码。对于现代 Web 开发,最常用的就是 UTF-8
在实际中的应用
  1. HTTP 协议: 如上所述,在 HTTP 请求和响应的头部中,Content-Type 字段至关重要。
  2. 电子邮件: 用于标识邮件正文和附件的格式。
  3. 文件系统: 某些操作系统会用 MIME 类型来关联打开文件的默认应用程序。
  4. &lt;link&gt; 和 &lt;script&gt; 标签: 在 HTML 中,你可以指定引入资源的类型。 <link rel="stylesheet" type="text/css" href="theme.css"> <script type="application/javascript" src="app.js"></script> (注意:在现代 HTML5 中,type 属性对于 CSS 和 JavaScript 通常可以省略,因为它们已是默认值。)