WebGL Tutorial
and more

处理二进制数据

撰写时间:2024-11-17

修订时间:2024-12-24

概述

在JavaScript中,二进制数据的核心载体是ArrayBufferArrayBuffer对外不提供访问其数据的接口,而是通过各种TypedArray,或DataView来访问及存储数据。

fetch函数,返回的Promise中经解析后可得到一个Response对象,可以将数据转换为诸如文本,XML, ArrayBufferBlob等类型的对象。其中Blob对象可以直接处理,也可以通过二进制流的方式来处理ArrayBuffer的数据。

TextEncoder, TextDecoder, FileReader等对象也提供了相应的方法来处理二进制数据。

本专题将循序渐进、集中地讲述JavaScript支持二进制数据的所有APIs. 所涉及到的对象及函数包括:

  • ArrayBuffer
  • Blob
  • File
  • FileReader
  • ReadableStream
  • WritableStream
  • TransformStream
  • TextDecoderStream
  • fetch

本专题有许多APIs均涉及到Promise,如果您对此不熟悉,建议先参阅Promise一文。

数值的存储与读取

对于任意一个数据,其数值的存储与读取是既独立而又相互影响的过程。

例如,对于十进制数值65,它可以表示一个整数,同时也可用以表示ASCII值为65的字母A

let intNum = 65; pc.log(intNum); let str = String.fromCodePoint(intNum); pc.log(str);

再比如,对于在内存中存储的同一个数值,如果按不同的数据类型来读取,则取出的的值有可能不一样。看下面的代码:

const {getBinStr} = await import('/js/esm/BinUtils.js'); let arrBuffer = new ArrayBuffer(1); let bufferWriter = new Uint8Array(arrBuffer); bufferWriter.set([0B1000_0100]); pc.log("raw binary:"); pc.log(getBinStr(bufferWriter[0], 4)); pc.log("unsigned int value:"); let u8Array = new Uint8Array(arrBuffer); pc.log(u8Array[0]); pc.log("signed int value:"); let i8Array = new Int8Array(arrBuffer); pc.log(i8Array[0]);

arraryBuffer先开辟了1个字节的内存空间,然后,通过bufferWriter,将这片内存区域的值设置为0x1000_0100。接着,分别按unsigned int类型及signed int的数据类型,从同样的内存区域中读取其值,则结果不一样。

如果不拘泥于内存区域,而只是强调同一个值,根据不同的数据类型,可以解读为不同的值,则可以编写下面更为简练的代码:

const { getBinStr } = await import('/js/esm/BinUtils.js'); let num = 0B1000_0100 pc.log("raw binary:"); pc.log(getBinStr(num, 4)); let u8Array = new Uint8Array([num]); pc.log("unsigned int value:"); pc.log(u8Array[0]); let i8Array = new Int8Array([num]); pc.log("signed int value:"); pc.log(i8Array[0]);

JavaScript是一种弱数据类型的编程语言,即在声明变量时无需指定数据类型,如:

let num1 = 132; let num2 = -124;

这得益于JavaScript编译器在后台帮我们做了大量的转换工作,让用户无需考虑繁琐的数据存储与读取的细节,从而让用户更专注于实现业务逻辑。但数据的存储与读取,尤其是读取,必须根据特定的数据类型才能正确地读取出来。JavaScript帮我们隐藏了这些细节。但数据的存储与读取的基本原理,任何一种编程语言都是一样的。

C语言则将这种强大的威力直接赋权于用户,一旦用户习惯了,则会由衷地感谢C语言的强大威力(参见内存存储的细节)。

两种语言,出发点不同,导致实现方式不同,用户在不同领域上的体验也不同。但均不失同为优秀的编程语言。

为何与二进制数据打交道

在刚开始出现时,作为一个脚本语言,JavaScript最主要的功能是用以操控网页的各个元素,为方便用户,许多涉及到计算机存储的细节均被隐藏了,大多数用户也不需要这些细节。这种环境下的JavaScript是一种较为封闭的编程语言。

但随着JavaScript功能被扩展,其应用领域越来越广泛,则能够读取、操作二进制流的需求呼声也越来越高,终于导致了许多能处置二进制流数据的功能出现。

例如,通过调用fetch函数,我们能加载各种外部资源。最早支持的资源类型包括:文本,XML, JSON等。对于这些结构较为固定的资源,JavaScript完全可以在内部消化处理。

但如果加载了二进制文件呢?任何数据都可以存储为二进制文件,而二进制文件的识别与读取,又依赖于创建二进制文件时所指定的特定格式,最常见的如各类图像格式的文件,如.gif.jpeg等,更进一步,再如扩展名为.glbglTF文件, 扩展名为.wasmWasm文件等,此时JavaScript在内部无法进行代劳了。

因此,JavaScript的语言功能进行了扩充,通过实现一系列相关的类或函数,全力支持二进制文件的创建、识别与读取。这意味着JavaScript此时已不再是一个纯粹的脚本语言,它已经有能力与外部各类资源进行无缝交互了。

对于用户来讲,对于任何二进制文件,只要我们能识别、读取,就可转换为可在JavaScript所支持的环境中安全地运行。电子表格、图像、C语言代码甚至汇编语言代码?JavaScript从此通吃不误。这是何等的威力、多高的价值!

参考资源

This site

  1. §ArrayBuffer in WebGL Tutorial