詳細聊聊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,這裡是為瞭簡化),curContext與A執行按位或操作:
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可以看作如下兩步操作:
- lanes取反(~lanes)
- 加1
為瞭直觀,用8位表示:
lanes 0001 0001 ~lanes 1110 1110 // 第一步 +1 1110 1111 // 第二步
則lanes & -lanes如下:
0001 0001 // lanes & 1110 1111 // -lanes ----------- 0000 0001
取到的就是第一位(已有更新中最高的優先級)。
總結
雖然業務中不常使用位操作,但在特定場景下位操作時很方便、高效的方式。
這波操作你愛瞭麼?
到此這篇關於React源碼中位運算技巧的文章就介紹到這瞭,更多相關React源碼中位運算技巧內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!