react|react 源码中位运算符的使用详解
位运算符基本使用
- 按位与(&):a & b对于每一个比特位,两个操作数都为 1 时, 结果为 1, 否则为 0
- 按位或(|):a | b对于每一个比特位,两个操作数都为 0 时, 结果为 0, 否则为 1
- 按位异或(^):a ^ b对于每一个比特位,两个操作数相同时, 结果为 1, 否则为 0
- 按位非(~):~ a反转操作数的比特位, 即 0 变成 1, 1 变成 0
0000 0000 0000 0000 0000 0000 0000 0011-> 31111 1111 1111 1111 1111 1111 1111 1100-> ~ 3 = -4
左移位<<:各二进位全部左移若干位,高位丢弃,低位补0。
0000 0000 0000 0000 0000 0000 0000 0110-> 60000 0000 0000 0000 0000 0000 0001 1000-> 6 << 2 = 24
【react|react 源码中位运算符的使用详解】右移位>>:各二进位全部右移若干位,正数高位补0,负数高位补1,低位丢弃
0000 0000 0000 0000 0000 0000 0000 1100-> 120000 0000 0000 0000 0000 0000 0000 0011-> 12 >> 2 = 3负数情况:1111 1111 1111 1111 1111 1111 1111 0100-> -121111 1111 1111 1111 1111 1111 1111 1101-> -12 >> 2 = -3
无符号右移位>>>:各二进位全部右移若干位,高位补0,低位丢弃。
0000 0000 0000 0000 0000 0000 0000 1100-> 120000 0000 0000 0000 0000 0000 0000 0011-> 12 >>> 2 = 31111 1111 1111 1111 1111 1111 1111 0100-> -120011 1111 1111 1111 1111 1111 1111 1101-> -12 >> 2 = 1073741821
正数计算负数:
取反后+1
+5:0101-5:取反1010 最低位+1 = 1011,1011即为二进制的-5
负数倒推正数:同样为取反后+1
在js中位运算的特点
- 位运算只能在整型变量之间进行运算
- js 中的Number类型在底层都是以浮点数(参考 IEEE754 标准)进行存储.
- js 中所有的按位操作符的操作数都会被转成补码(two’s complement)形式的有符号32位整数
- 操作数为浮点型时,转换流程: 浮点数 -> 整数(丢弃小数位) -> 位运算
- 操作数的大小超过Int32范围(-2^31 ~ 2^31-1). 超过范围的二进制位会被截断, 取低位32bit
- 另外由于 js 语言的隐式转换, 对非Number类型使用位运算操作符时会隐式会发生隐式转换, 相当于先使用Number(xxx)将其转换为number类型, 再进行位运算:
'str' >>> 0; //===> Number('str') >>> 0===> NaN >>> 0 = 0
位掩码
通过位移定义的一组枚举常量, 可以利用位掩码的特性, 快速操作这些枚举产量(增加, 删除, 比较)
const A = 1 << 0; // 0b00000001const B = 1 << 1; // 0b00000010const C = 1 << 2; // 0b00000100属性增加|ABC = A | B | C //0b00000111属性删除& ~AB = ABC & ~C //0b00000011属性比较AB 当中包含 B: AB & B === B。// AB & B =>0b00000010 ===B,trueAB 当中不包含 C: AB & C === 0 // AB & C =>0b00000000 === 0,true
react中的位运算
- react在涉及状态、标记位、优先级操作的地方大量使用了位运算
- react源码内部有多个上下文环境,在执行函数时经常需要判断当前处在哪个上下文环境中
// A上下文const A = 1; //0001// B上下文const B = 2; //0010// 当前所处上下文let curContext = 0; // 没有处在上下文的标志const NoContext = 0; 假设进入A的上下文curContext |= A; //即curContext=curContext|A =>0001判断是否处在某一上下文中,结合按位与操作与NoContext// 是否处在A上下文中,这里为true(curContext & A) !== NoContext //curContext & A)=>0001 !==0000,所以为true,表示在A的上下文中// 是否处在B上下文中,这里为false,和上方同理(curContext & B) !== NoContext 离开上下文,取出标记进行恢复// 从当前上下文中移除上下文AcurContext &= ~A; //curContext=curContext& ~A,即0001&1110=0000,进行恢复// 是否处在A上下文中,此处为false(curContext & A) !== NoContext //(curContext & A)为0000
ReactFiberLane.js
- 优先级定义
- 源码中变量只列出了 31 位, 由于 js 中位运算都会转换成Int32(上文已经解释), 最多为 32 位, 且最高位是符号位. 所以除去符号位, 最多只有 31 位可以参与运算
//类型定义export opaque type Lanes = number; export opaque type Lane = number; // 变量定义export const NoLanes: Lanes = /**/ 0b0000000000000000000000000000000; export const NoLane: Lane = /**/ 0b0000000000000000000000000000000; export const SyncLane: Lane = /**/ 0b0000000000000000000000000000001; export const SyncBatchedLane: Lane = /**/ 0b0000000000000000000000000000010; export const InputDiscreteHydrationLane: Lane = /**/ 0b0000000000000000000000000000100; const InputDiscreteLanes: Lanes = /**/ 0b0000000000000000000000000011000; const InputContinuousHydrationLane: Lane = /**/ 0b0000000000000000000000000100000; const InputContinuousLanes: Lanes = /**/ 0b0000000000000000000000011000000; // ...// ...const NonIdleLanes = /**/ 0b0000111111111111111111111111111; export const IdleHydrationLane: Lane = /**/ 0b0001000000000000000000000000000; const IdleLanes: Lanes = /**/ 0b0110000000000000000000000000000; export const OffscreenLane: Lane = /**/ 0b1000000000000000000000000000000;
getHighestPriorityLanes
function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes {// 判断 lanes中是否包含 SyncLaneif ((SyncLane & lanes) !== NoLanes) {return_highestLanePriority = SyncLanePriority; return SyncLane; }// 判断 lanes中是否包含 SyncBatchedLaneif ((SyncBatchedLane & lanes) !== NoLanes) {return_highestLanePriority = SyncBatchedLanePriority; return SyncBatchedLane; }// ...// ... 省略其他代码return lanes; }
getHighestPriorityLane
- react中处在越低bit位的更新优先级越高(越需要优先处理)
- 分离出最高优先级
- -lanes:表示负数的操作,即先取反然后+1
0b000 0000 0000 0000 0000 0000 0001 0001function getHighestPriorityLane(lanes) {return lanes & -lanes; }-lanse:lanes0001 0001~lanes 1110 1110 // 第一步+11110 1111 // 第二步0001 0001 // lanes& 1110 1111 // -lanes-----------0000 0001若lanes为0001 00000001 0000 // lanes& 1111 0000 // -lanes-----------0001 0000
getLowestPriorityLane
- 假设 lanes(InputDiscreteLanes) = 0b0000000000000000000000000011000
- 那么 clz32(lanes) = 27, 由于 InputDiscreteLanes 在源码中被书写成了 31 位, 虽然在字面上前导 0 是 26 个, 但是转成标准 32 位后是 27 个
- index = 31 - clz32(lanes) = 4
- 最后 1 << index = 0b0000000000000000000000000010000
- 相比最初的 InputDiscreteLanes, 分离出来了最左边的1
- 通过 lanes 的定义, 数字越小的优先级越高, 所以此方法可以获取最低优先级的 lane
function getLowestPriorityLane(lanes: Lanes): Lane {// This finds the most significant non-zero bit.const index = 31 - clz32(lanes); return index < 0 ? NoLanes : 1 << index; }
react-reconciler上下文定义
export const NoContext = /**/ 0b0000000; const BatchedContext = /**/ 0b0000001; const EventContext = /**/ 0b0000010; const DiscreteEventContext = /**/ 0b0000100; const LegacyUnbatchedContext = /**/ 0b0001000; const RenderContext = /**/ 0b0010000; const CommitContext = /**/ 0b0100000; export const RetryAfterError = /**/ 0b1000000; // ...// Describes where we are in the React execution stacklet executionContext: ExecutionContext = NoContext;
scheduleUpdateOnFiber
// scheduleUpdateOnFiber函数中包含了好多关于executionContext的判断(都是使用位运算)export function scheduleUpdateOnFiber(fiber: Fiber,lane: Lane,eventTime: number,) {if (root === workInProgressRoot) {// 判断: executionContext 不包含 RenderContextif (deferRenderPhaseUpdateToNextBatch ||(executionContext & RenderContext) === NoContext) {// ...}}if (lane === SyncLane) {if (// 判断: executionContext 包含 LegacyUnbatchedContext(executionContext & LegacyUnbatchedContext) !== NoContext &&// 判断: executionContext 不包含 RenderContext或CommitContext(executionContext & (RenderContext | CommitContext)) === NoContext) {// ...}}// ...}
- 在特定的情况下, 使用位运算不仅是提高运算速度, 且位掩码能简洁和清晰地表示出二进制变量之间的关系.
- 但是缺点也很明显, 不够直观, 扩展性不好(在 js 当中的二进制变量, 除去符号位, 最多只能使用 31 位, 当变量的数量超过 31 个就需要组合, 此时就会变得复杂)
推荐阅读
- react中的双向绑定你真的了解吗
- 你知道怎么在|你知道怎么在 HTML 页面中使用 React吗
- Vue3|Vue3 任务调度器 scheduler 源码分析
- HAVE|HAVE FUN | SOFARegistry 源码解析
- Three.js+React使二维图片呈现3D效果
- 【官方推荐】Laravel7|【官方推荐】Laravel7 + Vue2.0前后端分离框架通用后台源码
- 读Flink源码谈设计(FileSystemConnector中的整洁架构)
- Spring源码解析之八finishBeanFactoryInitialization方法即初始化单例bean
- Vue|Vue 源码解读(12)—— patch
- 【spring源码系列】之【Bean的销毁】