位运算的基础知识
所谓二进制,就是以二为底的一种计数方式。逢二进一
我门经常会使用二进制来进行计算。基于二进制的位运算能够很方便的表达 增删改查
比如我们后台管理系统,一般会针对权限的控制,一般这个使用的就是二进制
0b10000000
0b01000000
0b00100000
0b00010000
使用二进制来表示权限,首先速度上会更快一些,其次在表示多种权限的时候,会更加方便一些
比如我门有以下权限A、B、C,我们要根据不同的权限做不同的事情:
// 权限 A B C
if (value === "A") {
} else if (value === "B") {
} else if (value === "C") {
}
// 如果遇到既有A权限又有B权限 那么就要重新再定义个一个值
// 如果有很多 我们就要定义很多的值来处理
if(value === "AB"){
.....
}
在上面的代码中,会有一个问题,目前仅仅是一对一的关系,但是在实际开发中,往往有很多一对多的关系,也就是一个value可能会应对好几个值
在这种情况下,我们使用二进制会方便很多
二进制相关的运算
- 与 (&):只要有一位为0,最终就为0
- 或( | ):只要有一位为1,最终就为1
- 非(~):就是0 1 互换
- 异或(^):如果两个二进制位不相同,那么结果为1,相同为0
下面我们来看下位运算在权限系统的实际运用:
下载 | 打印 | 查看 | 审核 | 详情 | 删除 | 编辑 | 创建 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
其中 0就算代表没有权限,1代表有权限
0000 0001 表示有创建的权限
0000 0011 表示编辑和创建的权限
添加权限
直接使用或运算即可
0000 0011 表示编辑和创建的权限,再加一个打印的权限
0000 0011 | 0100 0011 = 0100 0011
删除权限
直接使用异或就可以
0100 0011 表示打印、编辑和创建的权限,我们要删除打印的权限
0100 0011 ^ 0100 0000 = 0000 0011
判断是不是有某一个权限
可以用与操作符来判断
0100 0011 表示打印、编辑和创建的权限,我们来判断是不是有打印的权限,有没有下载权限
0100 0000 & 0100 0000 = 0100 0000
判断是否有 打印的 权限,做 与 操作,得到了 打印权限的本身,说明就有这个权限
0100 0011 & 1000 0000 = 0000 0000
判断是否有 下载的 权限,做 与 操作,得到了 全为 0 ,说明就没有这个权限
React中的位运算
- fiber 的 flags
- lane 模型
- 上下文
fiber 的 flags
在React中,用来标记 fiber 操作的flags,使用的就是二进制
export const NoFlags = /* */ 0b000000000000000000000000000;
export const PerformedWork = /* */ 0b000000000000000000000000001;
export const Placement = /* */ 0b000000000000000000000000010;
export const DidCapture = /* */ 0b000000000000000000010000000;
export const Hydrating = /* */ 0b000000000000001000000000000;
// ...
之所以要专门抽离fiber状态,是因为这种操作是非常高效的。针对一个 fiber 的操作,可能有 增删改查操作,而是给这个 fiber 打上一个flag,接下来在后面的流程中针对有 flag 的fiber 统一进行操作
通过位运算,就可以很好的解决一个 fiber 有多个 flag 标记的问题,方便合并多个状态
export const NoFlags = /* */ 0b000000000000000000000000000;
export const PerformedWork = /* */ 0b000000000000000000000000001;
export const Placement = /* */ 0b000000000000000000000000010;
export const DidCapture = /* */ 0b000000000000000000010000000;
export const Hydrating = /* */ 0b000000000000001000000000000;
let flag = NoFlags;
// 合并多个 标记
flag = flag | PerformedWork | Placement;
if (flag & PerformedWork) {
console.log("PerformedWork");
}
if (flag & Placement) {
console.log("Placement");
}
lane 模型
是一套优先级机制,相对于 Scheduler,lane模型能够对任务进行更加细粒度的控制
const NoLanes= / 0b0000000000000000000000000000000;
const NoLane= / 0b0000000000000000000000000000000;
const SyncLane= / 0b0000000000000000000000000000001;
const SyncBatchedLane= / 0b0000000000000000000000000000010;
const InputDiscreteHydrationLane= / 0b0000000000000000000000000000100;
const InputDiscreteLanes= / 0b0000000000000000000000000011000;
const InputContinuousHydrationLane= / 0b0000000000000000000000000100000;
const InputContinuousLanes= / 0b0000000000000000000000011000000;
/**
*
* @param {*} lanes 一套lane的组合
* @returns
*/
function getHighestPriorityLanes(lanes){
// 从lanes的组合中,分离出优先级最高的 lane
switch (getHighestPriorityLane(lanes)){
case SyncLane:
return ''
}
}
lane 在表示优先级的时候,大致是这样的:
比如说我们有三个lane
0000 0001
0000 0010
0010 0000
那么lanes 就表示 一套lane的组合,组合到一起就表示 lanes 0010 0011
getHighestPriorityLane 这个方法就是表示要分离出优先级最高的
getHighestPriorityLane(0010 0011) ==> 0000 0001
function getHighestPriorityLane(lanes){
return lanes & -lanes
}
补码: 反码 + 1
const SyncLane = 0b0000000000000000000000000000001;
const InputContinuousLanes = 0b0000000000000000000000000000100;
lanes: SyncLane | InputContinuousLanes = 0b0000000000000000000000000000101;
-lanes: 0b1111111111111111111111111111011
lanes && -lanes = 0b0000000000000000000000000000001
上下文
在React源码内部,有多个上下文的
const NoContext = 0b000; // 位处于React上下文
const BatchedContext = 0b001; // 位处于Batched上下文
const RenderContext = 0b010; // 位处于Render上下文
const CommitContext = 0b100; // 位处于Commit上下文
// 上下文切换
let executionContext = NoContext;
executionContext |= RenderContext;
// 判断当前处于哪个上下文
(executionContext & RenderContext) !== NoContext;
(executionContext & CommitContext) !== NoContext;
// 判断是否移除某个上下文
executionContext &= ~RenderContext;