大家好,我是Kason。这两年有不少朋友向我吐槽过React的源码,比如:调度器为什么要用小顶堆这样的数据结构,而不是数组?源码中的各种单向链表和循环链表,直接用数组不行吗?源码中的各种位操作是否有必要?作为一个依赖业务的框架,为了提升一点点运行时的性能,React不惜将源码复杂化。在涉及状态、标志位和优先级操作的地方大量使用位操作。本文将对其中比较有代表性的一些进行讲解。学会后,遇到类似场景,一展身手,你就是业务线最美少年。JS中常用的几种位运算,位运算的操作数会转换为Int32(32位有符号整数),Int32对应位运算后的浮点数。在React中,主要有3种位运算符——按位与、按位或、按位非。如果两个二进制操作数的每一位都为1,则按位与(&)返回1,否则返回0。例如计算3&2,首先将操作数转换为Int32://Int320b0000000000000000000000000000011对应3//Int320b00000000000000000000000000000000010对应3//Int320b00000000000000000000000000000000010对应2的我们排除0,只保留后8位(实际参与计算的应该是32位):00000011&00000010------------00000010所以3&2的计算结果为2转换为浮点数。对两个二进制操作数的每一位按位或(|),如果都为0则结果为0,否则为1。计算10|3:00001010|00000011------------00001011转换为浮点数计算结果为11。按位非(~)对二进制操作数的每一位进行逐位取反运算(0和1互换)。对于~3,将3转成Int32再逐位取反://3对应Int320b0000000000000000000000000000011//逐位取反0b1111111111111111111111111111100计算结果为-4转换为浮点数。如果你对这个结果有疑问,可以去了解一下互补的知识。下面我们由易到难,看看位运算在React中的应用。React源码内部有多个上下文,在执行一个函数的时候,往往需要判断自己当前处于哪个上下文中。假设有三个上下文://AcontextconstA=1;//B上下文constB=2;//没有上下文constNoContext=0;当进入上下文时,可以使用按位或运算标志Enter://currentcontextletcurContext=0;//输入上下文curContext|=A;我们以8位二进制为例(同理,实际上应该是Int32,这里为了简化),curContext和A进行按位或运算:00000000//curContext|00000001//A------------00000001这时候可以结合按位与运算和NoContext来判断自己是否在某个上下文中://是否在A上下文中true(curContext&A)!==NoContext//是否在B上下文中false(curContext&B)!==NoContext离开某个上下文后,结合按位AND和按位NOT去除flags://从当前上下文中去除上下文AcurContext&=~A;//是否在A上下文中false(curContext&A)!==NoContextCurContext和~A进行按位与运算:00000001//curContext&11111110//~A----------00000000是从curContext中移除A。当业务中需要同时处理多个状态时,可以使用上层操作等技巧。优先级计算在React中,调用this.setState触发的更新在不同情况下会有不同的优先级。优先级之间的比较和选择也使用位操作。具体来说,React使用31位来存储更新(之所以是31而不是32是因为Int32的最高位是符号位,并不存储具体的数字)。低位的更新优先级越高(需要越优先处理)。例如,假设当前应用中有2个更新:0b0000000000000000000000000010001。第一个更新的优先级最高(需要同步处理),第五个为默认优先级。React经常需要找出当前最高优先级更新在哪一位(上例中的第一个),方法如下:functiongetHighestPriorityLane(lanes){returnlanes&-lanes;}说明,因为Int32使用补representation,所以-lanes可以看成下面两步操作:lanesinversion(~lanes)加1为了直观,用8bits表示:lanes00010001~lanes11101110//firststep+111101111//secondstep然后lanes&-lanes如下:00010001//lanes&11101111//-lanes------------00000001是第一个(现有更新中优先级最高的)。小结虽然位运算在业务中并不常用,但是在特定场景下进行位运算是一种方便高效的方式。这波操作你爱吗?欢迎加入人类优质前端框架研究组,带头
