乐趣区

关于javascript:React源码中的位运算技巧

大家好,我卡颂。

这两年有不少敌人和我吐槽 React 源码,比方:

  • 调度器为什么用小顶堆这种数据结构,间接用数组不行?
  • 源码里各种单向链表、环状链表,间接用数组不行?
  • 源码里各种位运算,有必要么?

作为业务依赖的框架,为了晋升一点点运行时性能,React从不吝惜将源码写的很简单。

在波及 状态 标记位 优先级 操作的中央大量应用了位运算。

本文会解说其中比拟有代表性的局部。学到之后,当遇到相似场景时露一手,你就是业务线最靓的仔。

几个罕用位运算

JS 中,位运算 的操作数会先转换为 Int32(32 位有符号整型),执行完 位运算 Int32对应浮点数。

React 中,次要用到 3 种位运算符 —— 按位与、按位或、按位非。

按位与(&)

对于两个二进制操作数的每个bit,如果都为 1,则后果为 1,否则为 0。

举个例子,计算3 & 2,首先将操作数转化为Int32

// 3 对应的 Int32
0b000 0000 0000 0000 0000 0000 0000 0011 
// 2 对应的 Int32
0b000 0000 0000 0000 0000 0000 0000 0010 

为了直观,咱们排除后面的 0,只保留最初 8 位(理论参加计算的应该是 32 位):

  0000 0011
& 0000 0010
-----------
  0000 0010

所以 3 & 2 计算结果转化为浮点数后为 2。

按位或(|)

对于两个二进制操作数的每个bit,如果都为 0,则后果为 0,否则为 1。

计算10 | 3

  0000 1010
| 0000 0011
-----------
  0000 1011

计算结果转化为浮点数后为 11。

按位非(~)

对一个二进制操作数的每个bit,逐位进行取反操作(0、1 调换)

对于 ~3,将 3 转化为Int32 后逐位取反:

// 3 对应的 Int32
0b000 0000 0000 0000 0000 0000 0000 0011 
// 逐位取反
0b111 1111 1111 1111 1111 1111 1111 1100

计算结果转化为浮点数后为 -4。

如果你对这个后果有纳闷,能够去理解 补码 相干常识

让咱们从易到难,看看位运算在 React 中的利用。

标记状态

React源码外部有多个上下文环境,在执行函数时常常须要判断以后处在哪个上下文环境中。

假如共有三种上下文状况:

// A 上下文
const A = 1;
// B 上下文
const B = 2;
// 没有处在上下文
const NoContext = 0;

当进入某个上下文时,能够应用 按位或 操作标记进入:

// 以后所处上下文
let curContext = 0;

// 进入 A 上下文
curContext |= A;

咱们用 8 位二进制举例(同样,理论应该是 Int32,这里是为了简化),curContextA 执行 按位或 操作:

  0000 0000  // curContext
| 0000 0001  // A
-----------
  0000 0001

此时能够联合 按位与 操作与 NoContext 来判断是否处在某一上下文中:

// 是否处在 A 上下文中 true
(curContext & A) !== NoContext

// 是否处在 B 上下文中 false
(curContext & B) !== NoContext

来到某上下文后,联合 按位与 按位非 移除标记:

// 从以后上下文中移除上下文 A
curContext &= ~A;

// 是否处在 A 上下文中 false
(curContext & A) !== NoContext

curContext~A 执行 按位与 操作:

  0000 0001  // curContext
& 1111 1110  // ~A
-----------
  0000 0000

即从 curContext 中移除A

当业务中须要同时解决多个状态时,能够应用如上位运算技巧。

优先级计算

React 中,不同状况下调用 this.setState 触发的更新会领有不同优先级。优先级之间的比拟、筛选同样应用了位运算。

具体来说,React中用 31 个 bit 位保留 更新 (之所以是 31 而不是 32 是因为Int32 的最高位是符号位,不保留具体的数)。

处在越低 bit 位的更新优先级越高(越须要优先解决)。

举个例子,假如以后利用存在 2 个更新:

0b000 0000 0000 0000 0000 0000 0001 0001

其中第 1 位的更新优先级最高(须要同步解决),第 5 位为默认优先级。

React常常须要找出以后最高优先级的更新在哪一位(如上例子中在第一位),办法如下:

function getHighestPriorityLane(lanes) {return lanes & -lanes;}

解释下,因为 Int32 采纳 补码 示意,所以 -lanes 能够看作如下两步操作:

  1. lanes 取反(~lanes)
  2. 加 1

为了直观,用 8 位示意:

lanes  0001 0001
~lanes 1110 1110 // 第一步
+1     1110 1111 // 第二步

lanes & -lanes 如下:

  0001 0001 // lanes  
& 1110 1111 // -lanes
-----------
  0000 0001

取到的就是第一位(已有更新中最高的优先级)。

总结

尽管业务中不常应用位操作,但在特定场景下位操作时很不便、高效的形式。

这波操作你爱了么?

欢送退出人类高质量前端框架钻研群,带飞

退出移动版