<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          React源碼中的位運(yùn)算技巧

          共 3393字,需瀏覽 7分鐘

           ·

          2021-11-27 21:44

          作者:卡頌

          簡(jiǎn)介:《React技術(shù)揭秘》作者

          來源:SegmentFault  思否社區(qū)


          大家好,我卡頌。


          這兩年有不少朋友和我吐槽 React 源碼,比如:


          • 調(diào)度器為什么用小頂堆這種數(shù)據(jù)結(jié)構(gòu),直接用數(shù)組不行?

          • 源碼里各種單向鏈表、環(huán)狀鏈表,直接用數(shù)組不行?

          • 源碼里各種位運(yùn)算,有必要么?


          作為業(yè)務(wù)依賴的框架,為了提升一點(diǎn)點(diǎn)運(yùn)行時(shí)性能,React
          從不吝惜將源碼寫的很復(fù)雜。


          在涉及狀態(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。

          計(jì)算10 | 3

            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)境中。

          假設(shè)共有三種上下文情況:

          // 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)化),curContextA執(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

          即從curContext中移除A

          當(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可以看作如下兩步操作:

          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

          取到的就是第一位(已有更新中最高的優(yōu)先級(jí))。

          總結(jié)



          雖然業(yè)務(wù)中不常使用位操作,但在特定場(chǎng)景下位操作時(shí)很方便、高效的方式。

          這波操作你愛了么?



          點(diǎn)擊左下角閱讀原文,到 SegmentFault 思否社區(qū) 和文章作者展開更多互動(dòng)和交流,掃描下方”二維碼“或在“公眾號(hào)后臺(tái)回復(fù)“ 入群 ”即可加入我們的技術(shù)交流群,收獲更多的技術(shù)文章~

          - END -

          瀏覽 35
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩AV中文字幕在线免费观看 | 国产视频国产区 | 国产人妻无码 | 97色网站在线观看免费视频 | 日韩黄色片免费看 |