1、ArrayBuffer、TypedArray、DataView
与其他语言相比,JavaScript 中的二进制数据是以非标准方式实现的。
ArrayBuffer:基本的二进制对象是 ArrayBuffer ,对固定长度的连续内存空间的引用
语法:
let buffer = new ArrayBuffer(16); // 分配一个 16 字节的连续内存空间 buffer,并用 0 进行预填充
alert(buffer.byteLength); // 16
特性:ArrayBuffer 是一个内存区域,只是一个原始的字节序列。ArrayBuffer 与 Array 没有任何共同之处
- 它的长度是固定的,我们无法增加或减少它的长度
- 它正好占用了内存中的那么多空间
- 要访问单个字节,需要另一个“视图”对象,而不是 buffer[index]
- 如果我们要写入值或遍历它,基本上几乎所有操作 —— 我们必须使用视图(view) ```typescript let buffer = new ArrayBuffer(16); // 创建一个长度为 16 的 buffer
let view = new Uint32Array(buffer); // 将 buffer 视为一个 32 位整数的序列
alert(Uint32Array.BYTES_PER_ELEMENT); // 每个整数 4 个字节
- TypedArray:一个**类型化数组**(TypedArray)对象描述了一个底层的二进制数据缓冲区(binary data buffer)的一个类数组**视图(view)**。没有名为 TypedArray 的构造器,它表示 ArrayBuffer上的视图之一的通用总称。
- 语法
- 当传入一个 buffer 参数,一个新的类型化数组视图将会被创建,并可用于呈现传入的 ArrayBuffer 实例。我们可以给定起始位置 byteOffset(默认为 0)以及 length(默认至 buffer 的末尾),这样视图将仅涵盖 buffer 的一部分。
```typescript
new TypedArray(); // ES2017中新增
new TypedArray(length);
new TypedArray(typedArray);
new TypedArray(object);
new TypedArray(buffer [, byteOffset [, length]]);
- 特性:类型化数组的行为类似于常规数组:具有索引,并且是可迭代的
- 在大多数情况下,我们直接对类型化数组进行创建和操作,而将 ArrayBuffer 作为“通用标识符(common discriminator)”隐藏起来。我们可以通过 .buffer 来访问它,并在需要时创建另一个视图。
- 属性
- TypedArray.BYTES_PER_ELEMENT:返回代表不同类型的类型化数组对象中,单个元素的字节大小
- TypedArray.length:类型化数组中元素的个数,例如 new Int8Array(3).length === 3
- TypedArray.name:返回一个字符串值,代表当前构造器的名称,例如 “Int8Array”
- TypedArray.buffer:返回在构造时被固定的TypedArray引用的ArrayBuffer
- TypedArray.byteLength :返回从其ArrayBuffer开始的TypedArray长度(以字节为单位)
- 切换视图 ```typescript let arr8 = new Uint8Array([0, 1, 2, 3]);
// 同一数据的另一个视图 let arr16 = new Uint16Array(arr8.buffer);
- DataView:**DataView 是在 ArrayBuffer 上的一种特殊的超灵活“未类型化”视图**。它允许以任何格式访问任何偏移量(offset)的数据。当我们将混合格式的数据存储在同一缓冲区(buffer)中时,DataView 非常有用
- 特性:通过 DataView,我们可以使用 .getUint8(i) 或 .getUint16(i) 之类的方法访问数据。我们在调用方法时选择格式,而不是在构造的时候。
- 语法:`new DataView(buffer, [byteOffset], [byteLength])`
```typescript
// 4 个字节的二进制数组,每个都是最大值 255
let buffer = new Uint8Array([255, 255, 255, 255]).buffer;
let dataView = new DataView(buffer);
// 在偏移量为 0 处获取 8 位数字
alert( dataView.getUint8(0) ); // 255
// 现在在偏移量为 0 处获取 16 位数字,它由 2 个字节组成,一起解析为 65535
alert( dataView.getUint16(0) ); // 65535(最大的 16 位无符号整数)
// 在偏移量为 0 处获取 32 位数字
alert( dataView.getUint32(0) ); // 4294967295(最大的 32 位无符号整数)
dataView.setUint32(0, 0); // 将 4 个字节的数字设为 0,即将所有字节都设为 0
1.1 8位
1.1.1 Int8Array
8 位二进制有符号整数,单个元素值的范围 -128 to 127,大小 1 bytes
1.1.2 Uint8Array
8 位无符号整数(超出范围后从另一边界循环),单个元素值的范围 0 to 255,大小 1 bytes。
- 特点:Uint8Array 数组创建时内容被初始化为0。创建完后,可以以对象的方式或使用数组下标索引的方式引用数组中的元素。
1.1.3 Uint8ClampedArray
8 位无符号整数(超出范围后为边界值),单个元素值的范围 0 to 255,大小 1 bytes
1.2 16位
1.2.1 Int16Array
16 位二进制有符号整数,单个元素值的范围 -32768 to 32767,大小 2 bytes
1.2.2 Uint16Array
16 位无符号整数,单个元素值的范围 0 to 65535,大小 2 bytes
1.3 32位
1.3.1 Int32Array
32 位二进制有符号整数,单个元素值的范围 -2147483648 to 2147483647,大小 4 bytes
1.3.2 Uint32Array
32 位无符号整数,单个元素值的范围 0 to 4294967295,大小 4 bytes
1.3.3 Float32Array
32 位 IEEE 浮点数(7 位有效数字,如 1.1234567),单个元素值的范围 1.2×10^-38 to 3.4×10^38,大小 4 bytes
1.4 64位
1.4.1 Float64Array
64 位 IEEE 浮点数(16 有效数字,如 1.123…15),单个元素值的范围 5.0×10^-324 to 1.8×10^308,大小 8 bytes
1.5 bigInt
1.5.1 BigInt64Array
64 位二进制有符号整数,单个元素值的范围 -2^63 to 2^63-1,大小 8 bytes
1.5.2 BigUint64Array
64 位无符号整数,单个元素值的范围 0 to 2^64-1,大小 8 bytes
2、Base64
Base64 是一组相似的二进制到文本(binary-to-text)的编码规则,使得二进制数据在解释成 radix-64 的表现形式后能够用 ASCII 字符串的格式表示出来。
- Base64编码普遍应用于需要通过被设计为处理文本数据的媒介上储存和传输二进制数据而需要编码该二进制数据的场景
- Base64 也被一些应用(包括使用 MIME 的电子邮件)和在 XML (en-US) 中储存复杂数据时使用
- 每一个Base64字符实际上代表着6比特位,因此,3字节(一字节是8比特,3字节也就是24比特)的字符串/二进制文件可以转换成4个Base64字符(4x6 = 24比特)。这意味着Base64格式的字符串或文件的尺寸约是原始尺寸的133%。如果编码的数据很少,增加的比例可能会更高
2.1 编码
WindowOrWorkerGlobalScope.btoa() 从 String 对象中创建一个 base-64 编码的 ASCII 字符串,其中字符串中的每个字符都被视为一个二进制数据字节
- 注意:由于这个函数将每个字符视为二进制数据的字节,而不管实际组成字符的字节数是多少,所以如果任何字符的码位超出 0x00 ~ 0xFF 这个范围,则会引发 InvalidCharacterError 异常
语法:
let encodedData = window.btoa(stringToEncode); //编码
2.2 解码
WindowOrWorkerGlobalScope.atob() 对经过 base-64 编码的字符串进行解码。你可以使用 window.btoa() 方法来编码一个可能在传输过程中出现问题的数据,并且在接受数据之后,使用 atob() 方法再将数据解码。例如:你可以编码、传输和解码操作各种字符,比如 0-31 的 ASCII 码值
语法:
let decodedData = window.atob(encodedData); // 解码
2.3 Unicode 问题
由于 DOMString 是16位编码的字符串,所以如果有字符超出了8位ASCII编码的字符范围时,在大多数的浏览器中对Unicode字符串调用 window.btoa 将会造成一个 Character Out Of Range 的异常。有很多种方法可以解决这个问题:
function b64EncodeUnicode(str) {
// first we use encodeURIComponent to get percent-encoded UTF-8,
// then we convert the percent encodings into raw bytes which
// can be fed into btoa.
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
function toSolidBytes(match, p1) {
return String.fromCharCode('0x' + p1);
}));
}
function b64DecodeUnicode(str) {
// Going backwards: from bytestream, to percent-encoding, to original string.
return decodeURIComponent(atob(str).split('').map(function(c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
}
注意:某些场景下,以上经由 UTF-8 转换到 Base64 的实现在空间利用上不一定高效。当处理包含大量 U+0800-U+FFFF 区域间字符的文本时, UTF-8 输出结果长于 UTF-16 的,因为这些字符在 UTF-8 下占用三个字节而 UTF-16 是两个。在处理均匀分布 UTF-16 码点的 JavaScript 字符串时应考虑采用 UTF-16 替代 UTF-8 作为 Base64 结果的中间编码格式,这将减少 40% 尺寸
3、URI
URI(统一资源标识符)是引用资源的字符串。
最常见的是 URL,它通过提供资源在 Web 上的位置来标识资源3.1 encodeURI、decodeURI
encodeURI() 函数通过将特定字符的每个实例替换为一个、两个、三或四个转义序列来对统一资源标识符 (URI) 进行编码 (该字符的 UTF-8 编码仅为四转义序列,由两个 “代理” 字符组成)。
- encodeURI 会替换所有的字符,但不包括以下字符,即使它们具有适当的UTF-8转义序列:
- 保留字符:; , / ? : @ & = + $
- 不转义字符:- _ . ! ~ * ‘ ( )
- 数字标志:#
- 字母数字:A-Z a-z 0-9
- decodeURI() 函数能解码由encodeURI 创建或其它流程得到的统一资源标识符(URI)
- 语法: ```typescript const uri = ‘https://mozilla.org/?x=шеллы‘; const encoded = encodeURI(uri); console.log(encoded); // expected output: “https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B“
try { console.log(decodeURI(encoded)); // expected output: “https://mozilla.org/?x=шеллы“ } catch (e) { // catches a malformed URI console.error(e); }
<a name="eyudX"></a>
### 3.2 encodeURIComponent、decodeURIComponent
- encodeURIComponent()函数通过将一个,两个,三个或四个表示字符的UTF-8编码的转义序列替换某些字符的每个实例来编码 URI (对于由两个“代理”字符组成的字符而言,将仅是四个转义序列)
- encodeURIComponent 转义除了如下所示外的所有字符: A-Z a-z 0-9 - _ . ! ~ * ' ( )
- 与encodeURI区别:额外转义保留字符、数字标志:**; , / ?** **: @ & = + $ ****#**
```typescript
let set1 = ";,/?:@&=+$"; // 保留字符
let set2 = "-_.!~*'()"; // 不转义字符
let set3 = "#"; // 数字标志
let set4 = "ABC abc 123"; // 字母数字字符和空格
console.log(encodeURI(set1)); // ;,/?:@&=+$
console.log(encodeURI(set2)); // -_.!~*'()
console.log(encodeURI(set3)); // #
console.log(encodeURI(set4)); // ABC%20abc%20123 (the space gets encoded as %20)
console.log(encodeURIComponent(set1)); // %3B%2C%2F%3F%3A%40%26%3D%2B%24
console.log(encodeURIComponent(set2)); // -_.!~*'()
console.log(encodeURIComponent(set3)); // %23
console.log(encodeURIComponent(set4)); // ABC%20abc%20123 (the space gets encoded as %20)
- 为了避免服务器收到不可预知的请求,对任何用户输入的作为URI部分的内容你都需要用encodeURIComponent进行转义
- decodeURIComponent() 方法用于解码由 encodeURIComponent 方法或者其它类似方法编码的部分统一资源标识符(URI)。
4、DataUrl、BlobUrl
4.1 DataUrl
dataUrl 即 data:[
- mediatype:一个MIME type的字符串,比如’image/jpeg’,默认是”text/plain;charset=US-ASCII”。canvas.toDataURL()并没有忽略,默认MIMIE type为”image/png”
[;base64]和:
- 如果data是纯文本,你可以简单的嵌入文本(根据document类型使用适当的实体或者转义)
- 如果data不是纯文本的话,可以标识为base64,并且嵌入base64编码的二进制数据
- base64字符串是用64进制来表示二进制数据的,它是一个ASCII字符串。由于仅仅是通过ASCII字符组成的,所以base64字符串是url-safe的,因此才将base64应用于data URL的中
4.2 BlobUrl
URL.createObjectURL() 静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的URL 对象表示指定的 File 对象或 Blob 对象
语法:
- object:用于创建 URL 的 File 对象、Blob 对象或者 MediaSource 对象
let objectURL = window.URL.createObjectURL(object);
window.URL.revokeObjectURL(objectURL);
- object:用于创建 URL 的 File 对象、Blob 对象或者 MediaSource 对象
内存管理
- 在每次调用 createObjectURL() 方法时,都会创建一个新的 URL 对象,即使你已经用相同的对象作为参数创建过。当不再需要这些 URL 对象时,每个对象必须通过调用 URL.revokeObjectURL() 方法来释放