位运算
MDN:位运算
JS对数字的存储是以IEEE-754
64位格式进行存储的,但是位运算并不会直接操作64位的值,而是先将64位转成32位进行计算,最后在将结果转换为64位。 如果转成2进制不足32位,则会转成先转成32位进行计算,不足的部分用0进行填充。如果超过则丢弃不要,在计算机中,最左边为高位,最右边为低位
本篇为了方便了解,全部采用32位进行标记
语法 | 例子 | 描述 |
按位与 AND | a & b | 在a,b的位表示中,每一个对应的位都为1则返回1, 否则返回0 |
按位或 OR | a | b | 在a,b的位表示中,每一个对应的位,只要有一个为1则返回1, 否则返回0 |
按位异或 XOR | a ^ b | 在a,b的位表示中,每一个对应的位,两个不相同则返回1,相同则返回0 |
按位非 NOT | ~a | 对每位进行取反 |
左移 | a << b | 将a的二进制串向左移动b位,低位空缺补0,高位溢出不要 左移不考虑正负数 |
右移 | a >> b | 把a的二进制表示向右移动b位 正数 右移的时候,最高位补0,低位舍去 负数 右移的时候,最高位补1,低位舍去 |
无符号右移 (左边空出位用0填充) | a >>> b | 把a的二进制表示向右移动b位,溢出舍去,高位补0(不处理正负数,高位不0) |
按位与 AND
- 每一位相同为1,不同为0
例子 | 二进制 | 结果/对应的二进制 |
5 & 5 | 0000 0000 0000 0000 0000 0000 0000 0101 0000 0000 0000 0000 0000 0000 0000 0101 | 5 0000 0000 0000 0000 0000 0000 0000 0101 |
5 & 10 | 0000 0000 0000 0000 0000 0000 0000 0101 0000 0000 0000 0000 0000 0000 0000 1010 | 5 0000 0000 0000 0000 0000 0000 0000 0000 |
按位或 OR
- 只要每个对应的位有1个是1就为1,否则为0
例子 二进制 结果/对应的二进制 10 | 22 0000 0000 0000 0000 0000 0000 0000 1010
0000 0000 0000 0000 0000 0000 0001 0110
30
0000 0000 0000 0000 0000 0000 0001 1110
按位异或 XOR
- 每个对应的位,不同为1,相同为0
例子 二进制 结果/对应的二进制 10 ^ 22 0000 0000 0000 0000 0000 0000 0000 1010
0000 0000 0000 0000 0000 0000 0001 0110
28
0000 0000 0000 0000 0000 0000 0001 1100
按位非 NOT
- 对每个位进行取反
注意:
- 在进行按位非运算时,所有数字的计算结果都是
~(x + 1)
由于对数字
~-1
和~4294967295
(232-1) 使用32位表示形式,结果均为0。
例子 | 二进制 | 结果/对应的二进制 |
~10 | 0000 0000 0000 0000 0000 0000 0000 1011 | -11 0000 0000 0000 0000 0000 0000 0000 0100 |
左移
- 向左移动n位,低位补0,高位不要
例子 | 二进制 | 结果/对应的二进制 |
2 << 2 | 2 0000 0000 0000 0000 0000 0000 0000 0010 | 8 0000 0000 0000 0000 0000 0000 0000 1000 |
右移
- 向右移动n位
例子 | 二进制 | 结果/对应的二进制 |
10 >> 2 | 10 0000 0000 0000 0000 0000 0000 0000 1010 | 2 0000 0000 0000 0000 0000 0000 0000 0010 |
无符号右移
- 把a的二进制表示向右移动b位,溢出舍去,高位补0(不处理正负数,高位不0
例子 | 二进制 | 结果/对应的二进制 |
54 >>> 2 | 54 0000 0000 0000 0000 0000 0000 0011 0110 | 13 0000 0000 0000 0000 0000 0000 0000 1101 |
vue-next对位运算的应用
在vue-next
中,充满了大量对位运算的操作,主要是针对一些内容做的标记,如:
- patchFlags 编译优化相关
- shapFlags 标记当前元素类型
- slotFlags slot相关
patchFlags
代码在packages/shared/src/patchFlags.ts
export const enum PatchFlags {
TEXT = 1, // 动态文本
CLASS = 1 << 1, // 2 动态class
STYLE = 1 << 2, // 4 动态style
PROPS = 1 << 3, // 8
FULL_PROPS = 1 << 4, // 16
HYDRATE_EVENTS = 1 << 5, // 32
STABLE_FRAGMENT = 1 << 6, // 64
KEYED_FRAGMENT = 1 << 7, // 128
UNKEYED_FRAGMENT = 1 << 8, // 256
NEED_PATCH = 1 << 9, // 512
DYNAMIC_SLOTS = 1 << 10, // 1024
DEV_ROOT_FRAGMENT = 1 << 11, // 2048
HOISTED = -1,
BAIL = -2
}
shapFlags
代码在packages/shared/src/shapeFlags.ts
export const enum ShapeFlags {
ELEMENT = 1,
FUNCTIONAL_COMPONENT = 1 << 1, // 2
STATEFUL_COMPONENT = 1 << 2, // 4
TEXT_CHILDREN = 1 << 3, // 8
ARRAY_CHILDREN = 1 << 4, // 16
SLOTS_CHILDREN = 1 << 5, // 32
TELEPORT = 1 << 6, // 64
SUSPENSE = 1 << 7, // 128
COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8, // 256
COMPONENT_KEPT_ALIVE = 1 << 9, // 512
COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT // 4 | 2
}
slotFlags
代码在packages/shared/src/slotFlags.ts
export const enum SlotFlags {
/**
* Stable slots that only reference slot props or context state. The slot
* can fully capture its own dependencies so when passed down the parent won't
* need to force the child to update.
*/
STABLE = 1,
/**
* Slots that reference scope variables (v-for or an outer slot prop), or
* has conditional structure (v-if, v-for). The parent will need to force
* the child to update because the slot does not fully capture its dependencies.
*/
DYNAMIC = 2,
/**
* `<slot/>` being forwarded into a child component. Whether the parent needs
* to update the child is dependent on what kind of slots the parent itself
* received. This has to be refined at runtime, when the child's vnode
* is being created (in `normalizeChildren`)
*/
FORWARDED = 3
}