作者:卡頌
簡(jiǎn)介:《React技術(shù)揭秘》作者
來源:SegmentFault 思否社區(qū)
大家好,我卡頌。
這兩年有不少朋友和我吐槽 React 源碼,比如:
作為業(yè)務(wù)依賴的框架,為了提升一點(diǎn)點(diǎn)運(yùn)行時(shí)性能,React在涉及狀態(tài)、標(biāo)記位、優(yōu)先級(jí)操作的地方大量使用了位運(yùn)算。
本文會(huì)講解其中比較有代表性的部分。學(xué)到之后,當(dāng)遇到類似場(chǎng)景時(shí)露一手,你就是業(yè)務(wù)線最靚的仔。幾個(gè)常用位運(yùn)算
在 JS 中,位運(yùn)算的操作數(shù)會(huì)先轉(zhuǎn)換為 Int32(32位有符號(hào)整型),執(zhí)行完位運(yùn)算會(huì)Int32對(duì)應(yīng)浮點(diǎn)數(shù)。在 React 中,主要用到3種位運(yùn)算符 —— 按位與、按位或、按位非。按位與(&)
對(duì)于兩個(gè)二進(jìn)制操作數(shù)的每個(gè) bit,如果都為1,則結(jié)果為1,否則為0。舉個(gè)例子,計(jì)算3 & 2,首先將操作數(shù)轉(zhuǎn)化為 Int32:// 3對(duì)應(yīng)的 Int32
0b000 0000 0000 0000 0000 0000 0000 0011
// 2對(duì)應(yīng)的 Int32
0b000 0000 0000 0000 0000 0000 0000 0010
為了直觀,我們排除前面的0,只保留最后8位(實(shí)際參與計(jì)算的應(yīng)該是32位): 0000 0011
& 0000 0010
-----------
0000 0010
所以3 & 2計(jì)算結(jié)果轉(zhuǎn)化為浮點(diǎn)數(shù)后為2。按位或(|)
對(duì)于兩個(gè)二進(jìn)制操作數(shù)的每個(gè)bit,如果都為0,則結(jié)果為0,否則為1。 0000 1010
| 0000 0011
-----------
0000 1011
計(jì)算結(jié)果轉(zhuǎn)化為浮點(diǎn)數(shù)后為11。按位非(~)
對(duì)一個(gè)二進(jìn)制操作數(shù)的每個(gè) bit,逐位進(jìn)行取反操作(0、1互換)對(duì)于~3,將3轉(zhuǎn)化為 Int32 后逐位取反:// 3對(duì)應(yīng)的 Int32
0b000 0000 0000 0000 0000 0000 0000 0011
// 逐位取反
0b111 1111 1111 1111 1111 1111 1111 1100
計(jì)算結(jié)果轉(zhuǎn)化為浮點(diǎn)數(shù)后為-4。如果你對(duì)這個(gè)結(jié)果有疑惑,可以去了解補(bǔ)碼相關(guān)知識(shí)讓我們從易到難,看看位運(yùn)算在React中的應(yīng)用。標(biāo)記狀態(tài)
React源碼內(nèi)部有多個(gè)上下文環(huán)境,在執(zhí)行函數(shù)時(shí)經(jīng)常需要判斷當(dāng)前處在哪個(gè)上下文環(huán)境中。// A上下文
const A = 1;
// B上下文
const B = 2;
// 沒有處在上下文
const NoContext = 0;
當(dāng)進(jìn)入某個(gè)上下文時(shí),可以使用按位或操作標(biāo)記進(jìn)入:// 當(dāng)前所處上下文
let curContext = 0;
// 進(jìn)入A上下文
curContext |= A;
我們用8位二進(jìn)制舉例(同樣,實(shí)際應(yīng)該是Int32,這里是為了簡(jiǎn)化),curContext與A執(zhí)行按位或操作: 0000 0000 // curContext
| 0000 0001 // A
-----------
0000 0001
此時(shí)可以結(jié)合按位與操作與NoContext來判斷是否處在某一上下文中:// 是否處在A上下文中 true
(curContext & A) !== NoContext
// 是否處在B上下文中 false
(curContext & B) !== NoContext
離開某上下文后,結(jié)合按位與、按位非移除標(biāo)記:// 從當(dāng)前上下文中移除上下文A
curContext &= ~A;
// 是否處在A上下文中 false
(curContext & A) !== NoContext
curContext與~A執(zhí)行按位與操作:0000 0001 // curContext
& 1111 1110 // ~A
-----------
0000 0000
當(dāng)業(yè)務(wù)中需要同時(shí)處理多個(gè)狀態(tài)時(shí),可以使用如上位運(yùn)算技巧。
優(yōu)先級(jí)計(jì)算
在React中,不同情況下調(diào)用this.setState觸發(fā)的更新會(huì)擁有不同優(yōu)先級(jí)。優(yōu)先級(jí)之間的比較、挑選同樣使用了位運(yùn)算。具體來說,React中用31個(gè)bit位保存更新(之所以是31而不是32是因?yàn)?/span>Int32的最高位是符號(hào)位,不保存具體的數(shù))。處在越低bit位的更新優(yōu)先級(jí)越高(越需要優(yōu)先處理)。
舉個(gè)例子,假設(shè)當(dāng)前應(yīng)用存在2個(gè)更新:0b000 0000 0000 0000 0000 0000 0001 0001
其中第1位的更新優(yōu)先級(jí)最高(需要同步處理),第5位為默認(rèn)優(yōu)先級(jí)。React經(jīng)常需要找出當(dāng)前最高優(yōu)先級(jí)的更新在哪一位(如上例子中在第一位),方法如下:function getHighestPriorityLane(lanes) {
return lanes & -lanes;
}
解釋下,由于Int32采用補(bǔ)碼表示,所以-lanes可以看作如下兩步操作:lanes取反(~lanes)
加1
lanes 0001 0001
~lanes 1110 1110 // 第一步
+1 1110 1111 // 第二步
0001 0001 // lanes
& 1110 1111 // -lanes
-----------
0000 0001
取到的就是第一位(已有更新中最高的優(yōu)先級(jí))。總結(jié)
雖然業(yè)務(wù)中不常使用位操作,但在特定場(chǎng)景下位操作時(shí)很方便、高效的方式。
點(diǎn)擊左下角閱讀原文,到 SegmentFault 思否社區(qū) 和文章作者展開更多互動(dòng)和交流,掃描下方”二維碼“或在“公眾號(hào)后臺(tái)“回復(fù)“ 入群 ”即可加入我們的技術(shù)交流群,收獲更多的技術(shù)文章~