詳細(xì)聊聊React源碼中的位運算技巧
前言
這兩年有不少朋友和我吐槽React源碼,比如:
- 調(diào)度器為什么用小頂堆這種數(shù)據(jù)結(jié)構(gòu),直接用數(shù)組不行?
- 源碼里各種單向鏈表、環(huán)狀鏈表,直接用數(shù)組不行?
- 源碼里各種位運算,有必要么?
作為業(yè)務(wù)依賴的框架,為了提升一點點運行時性能,React從不吝惜將源碼寫的很復(fù)雜。
在涉及狀態(tài)、標(biāo)記位、優(yōu)先級操作的地方大量使用了位運算。
本文會講解其中比較有代表性的部分。學(xué)到之后,當(dāng)遇到類似場景時露一手,你就是業(yè)務(wù)線最靚的仔。
幾個常用位運算
在JS中,位運算的操作數(shù)會先轉(zhuǎn)換為Int32(32位有符號整型),執(zhí)行完位運算會Int32對應(yīng)浮點數(shù)。
在React中,主要用到3種位運算符 —— 按位與、按位或、按位非。
按位與(&)
對于兩個二進制操作數(shù)的每個bit,如果都為1,則結(jié)果為1,否則為0。
舉個例子,計算3 & 2,首先將操作數(shù)轉(zhuǎn)化為Int32:
// 3對應(yīng)的 Int32 0b000 0000 0000 0000 0000 0000 0000 0011 // 2對應(yīng)的 Int32 0b000 0000 0000 0000 0000 0000 0000 0010
為了直觀,我們排除前面的0,只保留最后8位(實際參與計算的應(yīng)該是32位):
0000 0011 & 0000 0010 ----------- 0000 0010
所以3 & 2計算結(jié)果轉(zhuǎn)化為浮點數(shù)后為2。
按位或(|)
對于兩個二進制操作數(shù)的每個bit,如果都為0,則結(jié)果為0,否則為1。
計算10 | 3:
0000 1010 | 0000 0011 ----------- 0000 1011
計算結(jié)果轉(zhuǎn)化為浮點數(shù)后為11。
按位非(~)
對一個二進制操作數(shù)的每個bit,逐位進行取反操作(0、1互換)
對于~3,將3轉(zhuǎn)化為Int32后逐位取反:
// 3對應(yīng)的 Int32 0b000 0000 0000 0000 0000 0000 0000 0011 // 逐位取反 0b111 1111 1111 1111 1111 1111 1111 1100
計算結(jié)果轉(zhuǎn)化為浮點數(shù)后為-4。
如果你對這個結(jié)果有疑惑,可以去了解補碼相關(guān)知識
讓我們從易到難,看看位運算在React中的應(yīng)用。
標(biāo)記狀態(tài)
React源碼內(nèi)部有多個上下文環(huán)境,在執(zhí)行函數(shù)時經(jīng)常需要判斷當(dāng)前處在哪個上下文環(huán)境中。
假設(shè)共有三種上下文情況:
// A上下文 const A = 1; // B上下文 const B = 2; // 沒有處在上下文 const NoContext = 0;
當(dāng)進入某個上下文時,可以使用按位或操作標(biāo)記進入:
// 當(dāng)前所處上下文 let curContext = 0; // 進入A上下文 curContext |= A;
我們用8位二進制舉例(同樣,實際應(yīng)該是Int32,這里是為了簡化),curContext與A執(zhí)行按位或操作:
0000 0000 // curContext | 0000 0001 // A ----------- 0000 0001
此時可以結(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
即從curContext中移除A。
當(dāng)業(yè)務(wù)中需要同時處理多個狀態(tài)時,可以使用如上位運算技巧。
優(yōu)先級計算
在React中,不同情況下調(diào)用this.setState觸發(fā)的更新會擁有不同優(yōu)先級。優(yōu)先級之間的比較、挑選同樣使用了位運算。
具體來說,React中用31個bit位保存更新(之所以是31而不是32是因為Int32的最高位是符號位,不保存具體的數(shù))。
處在越低bit位的更新優(yōu)先級越高(越需要優(yōu)先處理)。
舉個例子,假設(shè)當(dāng)前應(yīng)用存在2個更新:
0b000 0000 0000 0000 0000 0000 0001 0001
其中第1位的更新優(yōu)先級最高(需要同步處理),第5位為默認(rèn)優(yōu)先級。
React經(jīng)常需要找出當(dāng)前最高優(yōu)先級的更新在哪一位(如上例子中在第一位),方法如下:
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
取到的就是第一位(已有更新中最高的優(yōu)先級)。
總結(jié)
雖然業(yè)務(wù)中不常使用位操作,但在特定場景下位操作時很方便、高效的方式。
這波操作你愛了么?
到此這篇關(guān)于React源碼中位運算技巧的文章就介紹到這了,更多相關(guān)React源碼中位運算技巧內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一文詳解ReactNative狀態(tài)管理redux-toolkit使用
這篇文章主要為大家介紹了ReactNative狀態(tài)管理redux-toolkit使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03
用React實現(xiàn)一個完整的TodoList的示例代碼
本篇文章主要介紹了用React實現(xiàn)一個完整的TodoList的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10
基于React.js實現(xiàn)兔兔牌九宮格翻牌抽獎組件
這篇文章主要為大家詳細(xì)介紹了如何基于React.js實現(xiàn)兔兔牌九宮格翻牌抽獎組件,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2023-01-01
React+umi+typeScript創(chuàng)建項目的過程
這篇文章主要介紹了React+umi+typeScript創(chuàng)建項目的過程,結(jié)合代碼介紹了項目框架搭建的方式,本文給大家講解的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-02-02
D3.js(v3)+react 實現(xiàn)帶坐標(biāo)與比例尺的散點圖 (V3版本)
散點圖(Scatter Chart),通常是一橫一豎兩個坐標(biāo)軸,數(shù)據(jù)是一組二維坐標(biāo),分別對應(yīng)兩個坐標(biāo)軸,與坐標(biāo)軸對應(yīng)的地方打上點。由此可以猜到,需要的元素包括circle(圓)和axis(坐標(biāo)軸),接下來通過本文大家分享D3.js(v3)+react 實現(xiàn)帶坐標(biāo)與比例尺的散點圖 (V3版本) ,一起看看2019-05-05

