Web 进制操作是一个比较底层的话题,因为平常做业务的时候根本用不到太多,或者说,根本用不到。 老铁,没毛病 那什么情况会用到呢? - canvas
- websocket
- file
- fetch
- webgl
上面只是列了部分内容。现在比较流行的就是音视频的处理,怎么说呢? 如果,有涉及直播的话,那么这应该就是一个非常!非常!非常!重要的一块内容。我这里就不废话了,先主要看一下里面的基础内容。 整体架构首先,一开始我们是怎么接触到底层的 bit 流呢? 记住:只有一个对象我们可以搞到 bit 流 --> ArrayBuffer 这很似曾相识,例如在 fetch 使用中,我们可以通过 res.arrayBuffer(); 来直接获取 ArrayBuffer 对象。websocket 中,监听 message ,返回来的 event.data 也是 arraybuffer。 let socket = new WebSocket('ws://127.0.0.1:8080');
socket.binaryType = 'arraybuffer';
socket.addEventListener('message', function (event) {
let arrayBuffer = event.data;
···
});但是,ArrayBuffer 并不能直接提供底层流的获取操作!!! 你可以通过 TypeArray 和 DataView 进行相关查看: 
接下来,我们具体看一下 TypeArray 和 DataView 的具体细节吧。 TypedArray首先声明这并不是一个具体的 array 对象,而是一整个底层 Buffer 的概念集合。首先,我们了解一下底层的二进制: 二进制在一般程序语言里面,最底层的数据大概就可以用 0 和 1 来表示: 根据底层的比特的数据还可以划分为两类: - signed: 从左到右第一位开始,如果为 0 则表示为正,为 1 则表示为负。例如:-127~+127
- unsigned: 从左到右第一位不作为符号的表示。例如:0~255
而我们程序表达时,为了易读性和简便性常常会结合其他进制一起使用。 - 八进制(octet)
- 十进制(Decimal)
- 十六进制(Hexadecimal)
特别提醒的是: 在 JS 中: 使用 0x 字面上表示十六进制。每一位代表 4bit(2^4)。 使用 0o 字面上表示八进制。每一位代表 3bit(2^3)。还有一种是直接使用 0 为开头,不过该种 bug 较多,不推荐。 使用 0b字面上表示二进制。每一位代表 1bit(2^1)。 了解了二进制之后,接下来我们主要来了解一下 Web 比特位运算的基本内容。 位运算Web 中的位运算和其它语言中类似,有基本的 7 个。 与 ( & )在相同位上,都为 1 时,结果才为 1:
001 & 101 = 001 并且,该运算常常会和叫做 bitmask (屏蔽字)结合起来使用。比如,在音视频的 Buffer 中,第 4 位 bit 表示该 media segments 里面是否存在 video。那么为了检验,则需要提取第 4 位,这时候就需要用到我们的 bitmask。
buf & 8 或 ( | )在相同位上,有一个为 1 时,结果为 1。
9 (base 10) = 00000000000000000000000000001001 (base 2)
14 (base 10) = 00000000000000000000000000001110 (base 2)
--------------------------------
14 ^ 9 (base 10) = 00000000000000000000000000000111 (base 2) = 7 (base 10)非 ( ~ )只和自己做运算,如果为 0,结果为 1。如果为 1 结果为 0。反正就是相反的意思了:
9 (base 10) = 00000000000000000000000000001001 (base 2)
--------------------------------
~9 (base 10) = 11111111111111111111111111110110 (base 2) = -10 (base 10)异或 ( ^ )当两者中只有一个 1 那么结果才为 1。
9 (base 10) = 00000000000000000000000000001001 (base 2)
14 (base 10) = 00000000000000000000000000001110 (base 2)
--------------------------------
14 ^ 9 (base 10) = 00000000000000000000000000000111 (base 2) = 7 (base 10)左移 ( << )基本格式为:x << y 将 x 向左移动 y 位数。空出来的补 0
9 (base 10): 00000000000000000000000000001001 (base 2)
--------------------------------
9 << 2 (base 10): 00000000000000000000000000100100 (base 2) = 36 (base 10)带位右移 ( >> )什么叫 带位 呢? 上面我们提到过 signed 和 unsigned 。那么这里针对的就是 signed 的位移类型。 格式为: x >> y 将 x 向右移动 y 位数。左边空出来的位置根据最左边的第一位决定,如果为 1 则补 1,反之。 1001 >> 2 = 1110 直接右移 ( >>> )该方式和上面具体区别就是,该运算针对的是 unsigned 的移动。不管你左边是啥,都给我补上 0。 格式为: x >> y 1001 >> 2 = 0010 上面这些运算符主要是针对 32bit 的。不过有时候为了简便,可以省去前面多余的 0。不过大家要清楚,这是针对 32 位的即可。 优先级上面简单介绍了位操作符,但是他们的优先级是怎么样的呢?详情可以参考: precedence ; 简单来说:(按照下列顺序,优先级降低) ~ >> << >>> & ^ | 位运算具体运用状态改变后台在保存数据的时候,常常会遇到某一个字段有多种状态。例如,填表状态:填完,未填,少填,填错等。一般情况下直接用数字来进行代替就行,只要文档写清楚就没事。例如: 不过,我们还可以通过比特位来进行表示,每一位表示一个具体的状态。 - 0001: 填完
- 0010: 未填
- 0100:少填
- 1000:填错
这样我们只要找到每一位是否为 1 就可以知道里面有哪些状态存在。并且,还可以对状态进行组合,例如,填完并且填错,如果按照数字来说就没啥说明这样的情况。 那么基本的状态值有了,接下来就是怎么进行赋值和修改。 现在假设,某人的填写状态为 填完 + 填错。那么结果可以表示为: var mask = 0001 | 1000; 后面如果涉及条件判断,例如:该人是否填错,则可以使用 & 来表示:
if(mask & 1000) doSth; 或者,是否即填完又填错 if(mask & (1000 | 0001)) doSth 后面涉及到状态改变的话,则需要用到 | 运算。假设,现在该人为填完,现在变为少填。那么状态改变应该为:
var done = ~0001;
mask &= done;
mask |= 0100 进制转换在 JS 中进制转换有两种方式: toString 和 parseInt 。 - toString(radix): 该可以将任意进制转换为 2-36 的进制。radix 默认为 10。
- parseInt(string,radix): 将指定 string 根据 radix 的标识转换成为 10 进制。radix 默认为 10。另外它主要用作于字符串的提取。
- Number(string): 字面上转换字符串为十进制。
parseInt 用于字符串过滤,例如: parseInt('15px', 10); 里面的字符不仅只有数字,而且还包括字母。 不过需要注意的是,parseInt 是不认可,以 0 开头的八进制,但认可 0o。所以,在使用的时候需要额外注意。 上面说过,parseInt 是将其它进制转换为 10 进制,其第二个参数主要就是为了表示前面内容的进制,如果没写,引擎内部会进行相关识别,但不保证一定正确。所以,最好写上。 parseInt(' 0xF', 16); 如果你只是想简单转换一下字符串,那么使用 Number() 无疑是最简单的。 Number('0x11')
Number('0b11')
Number('0o11') toStringtoString 里面的坑就没有 parseInt 这么多了。它也是进制转换非常好用的一个工具。因为是 字符串 ,所以,这里就只能针对字面量进制进行转换了–2,8,(10),16。这四种进制的相关之间转换。 提醒:如果你是直接使用字面量转换的话,需要注意使用 10 进制转换时,隐式转换会失效。即,100.toString(2) 会报错。 例如: 0b1101101.toString(8);
0b1101101.toString(10);
0b1101101.toString(8); 如上面所述,他们转换后的结果一般没有进制前缀。这个时候,就需要手动加上相关的前缀即可。 例如:16 进制转换 function hexConvert(str){
return "0x" + str.toString(16);
}到这里,进制转换基本就讲完了。后面我们来看一下具体的 TypeArray 整体架构TypeArray 不是一个可以用程序写出来的概念,它是许多 TypeArray 的总称。参考: TypeArray。可以了解到,它的子类如下: - Int8Array();
- Uint8Array();
- Uint8ClampedArray();
- Int16Array();
- Uint16Array();
- Int32Array();
- Uint32Array();
- Float32Array();
- Float64Array();
看上去很多,不过在 JS 中,因为它天生都不是用来处理 signed 类型的。所以, Uint 系列在 JS 中应该算是主流。大概排个序: Uint8Array > Uint16Array > Int8Array > … 他们之间的具体不同,参照: |数据类型| 字节长度| 含义| 对应的C语言类型| |:—|:—|:—| |Int8| 1| 8位带符号整数| signed char| |Uint8| 1| 8位不带符号整数| unsigned char| |Uint8C| 1| 8位不带符号整数(自动过滤溢出)| unsigned char| |Int16| 2| 16位带符号整数| short| |Uint16| 2| 16位不带符号整数| unsigned short| |Int32| 4| 32位带符号整数| int| |Uint32| 4| 32位不带符号的整数| unsigned int| |Float32| 4| 32位浮点数| float| |Float64| 8| 64位浮点数| double| 虽然口头上说 TypeArray 没有一个具体的实例,但是私下,上面那几个 array 都是叫他爸爸。因为他定义了一些 uintArray 的基本功能。首先是实例化: TypeArray 的实例化有 4 种: new TypedArray(length);
new TypedArray(typedArray);
new TypedArray(object);
new TypedArray(buffer [, byteOffset [, length]]); 上面 4 中最常用的应该为 1 和 4。接着,我们了解一下,具体才创建的时候,TypeArray 到底做了些什么。 当创建实例 TypeArray 的构造函数时,内部会同时创建一个 arrayBuffer 用来作为数据的存储。如果是通过 TypedArray(buffer); 方式创建,那么 TypeArray 会直接使用该 buffer 的内存地址。 接下来,我们就以
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|