JS的二进制操作

17次阅读

共计 3919 个字符,预计需要花费 10 分钟才能阅读完成。

之前在某个项目中,遇到了许多 JS 的二进制操作场景,因此总结下 JS 中的二进制操作方法。
所谓二进制操作,是指操作变量实际存储的值,比如获取字符 A 的 Unicode 值,或者将值 100 填入到 8 个字节中。
1. 位操作符
JS 中的位操作与很多语言类似,具体的位运算符如下表所示。

运算符
用法
描述

按位与
a & b
对于每一个比特位,只有两个操作数相应的比特位都是 1 时,结果才为 1,否则为 0。

按位或
a | b
对于每一个比特位,当两个操作数相应的比特位至少有一个 1 时,结果为 1,否则为 0。

按位异或
a ^ b
对于每一个比特位,当两个操作数相应的比特位有且只有一个 1 时,结果为 1,否则为 0。

按位非
~ a
反转操作数的比特位,即 0 变成 1,1 变成 0。

左移
a << b
将 a 的二进制形式向左移 b (< 32) 比特位,右边用 0 填充。

有符号右移
a >> b
将 a 的二进制表示向右移 b (< 32) 位,丢弃被移出的位。

无符号右移
a >>> b
将 a 的二进制表示向右移 b (< 32) 位,丢弃被移出的位,并使用 0 在左侧填充。

2. 字符和 Unicode 编码
在介绍具体的方法前,我们需要先了解下 UCS- 2 和 UTF-16 编码。
UCS- 2 是一个 16bit 长度的编码集,它的表示范围是 0 到 0xFFFF。UTF-16 的表示范围是 0 到 0x10FFFF,它由 1 个或者 2 个 16bit 的编码单元组成。其中 UCS- 2 是 UTF-16 的子集,UTF-16 编码在 0 到 0x00FFFF 的范围称为 BMP(基本多文种平面),BMP 与 UCS- 2 的编码完全一致。
更详细的说明可以参考这里。
2.1 String.fromCharCode
fromCharCode 方法返回指定的 UCS- 2 编码对应的字符串。它是 String 上的静态方法,不可通过字符串对象直接访问。
因为入参是 UCS- 2 编码值,所以不能多于 16bit,即入参值要小于 65536。如果入参需要大于 65536,可以使用 String.fromCodePoint。
String.fromCharCode(65) // A
String.fromCharCode(65, 66, 68) // ABD
2.2 String.prototype.charCodeAt
charCodeAt 返回字符串指定位置的字符的 UTF-16 编码。该方法可以直接从字符串对象进行调用。
如果该字符不能使用一个 UTF-16 编码单元(16bit)来表示时,该方法只会返回第一个编码单元。如果需要获取完整的编码,可以使用 String.prototype.codePointAt。
“AB”.charCodeAt(0) // 65
“AB”.charCodeAt(1) // 66
3. ArrayBuffer
ArrayBuffer 用来表示原始的二进制数据缓存区,但是不可直接对 ArrayBuffer 进行操作,需要借助 DataView 或者类型数组对象来对缓存区的内容进行读写。
3.1 DataView
DataView 可以理解为数据视窗,通过 DataView 对象可以对 ArrayBuffer 进行读写操作。
const buffer = new ArrayBuffer(4); // 申请 2 个字节长度的缓存区
const view1 = new DataView(buffer); // view1 的范围是整个缓存区
const view2 = new DataView(buffer, 2, 1) // view2 的范围是从第 2 个字节开始往后的一个字节

// 向一个 16bit 的内容中填入一个带符号的数
// 参数的含义依次为 输入内容的位置、输入的值、是否使用小端方式 (默认大端)
view1.setInt16(0, 0x0A0B, false);
view1.getInt8(0); // 10,即 0x0A
view1.getInt8(1); // 11,即 0x0B

view2.setUint8(0, 255);
view2.getInt8(0); // 按照有符号数来读取,结果为 -1
更多的操作方法可以参考 DataView。
3.2 类型数组对象
类型数组对象有很多种,比如 Uint8Array, Int32Array 等。将 ArrayBuffer 转化为类型数组后,就可以像数组一样来操作缓存区。
const buffer = new ArrayBuffer(8);

const arr1 = new Int16Array(buffer);
const arr2 = new Uint8Array(buffer);

arr1[0] = 256;
arr2[7] = 255;

console.log(arr1); // [256, 0, 0, 255]
console.log(arr2); // [0, 1, 0, 0, 0, 0, 255, 0]
参考文献

按位操作符 – JavaScript | MDN
[Unicode 字符平面映射](之前在某个项目中,遇到了许多 JS 的二进制操作场景,因此总结下 JS 中的二进制操作方法。

所谓二进制操作,是指操作变量实际存储的值,比如获取字符 A 的 Unicode 值,或者将值 100 填入到 8 个字节中。
1. 位操作符
JS 中的位操作与很多语言类似,具体的位运算符如下表所示。

运算符
用法
描述

按位与
a & b
对于每一个比特位,只有两个操作数相应的比特位都是 1 时,结果才为 1,否则为 0。

按位或
a | b
对于每一个比特位,当两个操作数相应的比特位至少有一个 1 时,结果为 1,否则为 0。

按位异或
a ^ b
对于每一个比特位,当两个操作数相应的比特位有且只有一个 1 时,结果为 1,否则为 0。

按位非
~ a
反转操作数的比特位,即 0 变成 1,1 变成 0。

左移
a << b
将 a 的二进制形式向左移 b (< 32) 比特位,右边用 0 填充。

有符号右移
a >> b
将 a 的二进制表示向右移 b (< 32) 位,丢弃被移出的位。

无符号右移
a >>> b
将 a 的二进制表示向右移 b (< 32) 位,丢弃被移出的位,并使用 0 在左侧填充。

2. 字符和 Unicode 编码
在介绍具体的方法前,我们需要先了解下 UCS- 2 和 UTF-16 编码。
UCS- 2 是一个 16bit 长度的编码集,它的表示范围是 0 到 0xFFFF。UTF-16 的表示范围是 0 到 0x10FFFF,它由 1 个或者 2 个 16bit 的编码单元组成。UCS- 2 是 UTF-16 的子集,当字符的编码范围在 0x00FFFF。
更详细的说明可以参考这里。
2.1 String.fromCharCode
fromCharCode 方法返回指定 UCS- 2 编码对应的字符串。它是 String 上的静态方法,不可通过字符串对象直接访问。
所以 fromCharCode 的入参值要小于 65536。否则需要使用 String.fromCodePoint 代替。
String.fromCharCode(65) // A
String.fromCharCode(65, 66, 68) // ABD
2.2 String.prototype.charCodeAt
charCodeAt 返回字符串指定位置的字符的 UTF-16 编码。该方法可以直接从字符串对象进行调用。
如果该字符不能使用一个 UTF-16 编码单元(16bit)来表示时,该方法只会返回第一个编码单元。如果需要获取完整的编码,需要使用 String.prototype.codePointAt 代替。
“AB”.charCodeAt(0) // 65
“AB”.charCodeAt(1) // 66
PS:
3. ArrayBuffer
ArrayBuffer 用来表示原始的二进制数据缓存区,类似于申请一块内存空间,但是不可直接对缓存区进行操作,需要借助 DataView 或者类型数组对象来对缓存区的内容进行读写。
3.1 DataView
DataView 可以理解为数据视窗,通过 DataView 对象可以对 ArrayBuffer 进行读写操作。
const buffer = new ArrayBuffer(4); // 申请 2 个字节长度的缓存区
const view1 = new DataView(buffer); // view1 的范围是整个缓存区
const view2 = new DataView(buffer, 2, 1) // view2 的范围是从第 2 个字节开始往后的一个字节

// 向一个 16bit 的内容中填入一个带符号的数
// 参数的含义依次为 输入内容的位置、输入的值、是否使用小端方式 (默认大端)
view1.setInt16(0, 0x0A0B, false);
view1.getInt8(0); // 10,即 0x0A
view1.getInt8(1); // 11,即 0x0B

view2.setUint8(0, 255);
view2.getInt8(0); // 按照有符号数来读取,结果为 -1
更多的操作方法可以参考 DataView。
3.2 类型数组对象
类型数组对象有很多种,比如 Uint8Array, Int32Array 等。将 ArrayBuffer 转化为类型数组后,就可以像数组一样来操作缓存区。
const buffer = new ArrayBuffer(8);

const arr1 = new Int16Array(buffer);
const arr2 = new Uint8Array(buffer);

arr1[0] = 256;
arr2[7] = 255;

console.log(arr1); // [256, 0, 0, 255]
console.log(arr2); // [0, 1, 0, 0, 0, 0, 255, 0]
参考文献

按位操作符 – JavaScript | MDN
Unicode 字符平面映射
JavaScript’s internal character encoding: UCS-2 or UTF-16?
String.fromCharCode() – JavaScript | MDN
String.prototype.charCodeAt() – JavaScript | MDN
ArrayBuffer – JavaScript | MDN

正文完
 0