日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

react?源碼中位運算符的使用詳解_React

作者:神奇大叔 ? 更新時間: 2022-05-07 編程語言

位運算符基本使用

  • 按位與(&):a & b對于每一個比特位,兩個操作數都為 1 時, 結果為 1, 否則為 0
  • 按位或(|):a | b對于每一個比特位,兩個操作數都為 0 時, 結果為 0, 否則為 1
  • 按位異或(^):a ^ b對于每一個比特位,兩個操作數相同時, 結果為 1, 否則為 0
  • 按位非(~):~ a反轉操作數的比特位, 即 0 變成 1, 1 變成 0
0000 0000 0000 0000 0000 0000 0000 0011     -> 3
1111 1111 1111 1111 1111 1111 1111 1100     -> ~ 3 = -4

左移位<<:各二進位全部左移若干位,高位丟棄,低位補0。

0000 0000 0000 0000 0000 0000 0000 0110     -> 6
0000 0000 0000 0000 0000 0000 0001 1000     -> 6 << 2 = 24

右移位>>:各二進位全部右移若干位,正數高位補0,負數高位補1,低位丟棄

0000 0000 0000 0000 0000 0000 0000 1100     -> 12
0000 0000 0000 0000 0000 0000 0000 0011     -> 12 >> 2 = 3
負數情況:
1111 1111 1111 1111 1111 1111 1111 0100    -> -12
1111 1111 1111 1111 1111 1111 1111 1101    -> -12 >> 2 = -3

無符號右移位>>>:各二進位全部右移若干位,高位補0,低位丟棄。

0000 0000 0000 0000 0000 0000 0000 1100     -> 12
0000 0000 0000 0000 0000 0000 0000 0011     -> 12 >>> 2 = 3
1111 1111 1111 1111 1111 1111 1111 0100    -> -12
0011 1111 1111 1111 1111 1111 1111 1101    -> -12 >> 2 = 1073741821

正數計算負數:

取反后+1

+5:0101
-5:取反1010 最低位+1 = 1011,1011即為二進制的-5

負數倒推正數:同樣為取反后+1

在js中位運算的特點

  • 位運算只能在整型變量之間進行運算
  • js 中的Number類型在底層都是以浮點數(參考 IEEE754 標準)進行存儲.
  • js 中所有的按位操作符的操作數都會被轉成補碼(two’s complement)形式的有符號32位整數
  • 操作數為浮點型時,轉換流程: 浮點數 -> 整數(丟棄小數位) -> 位運算
  • 操作數的大小超過Int32范圍(-2^31 ~ 2^31-1). 超過范圍的二進制位會被截斷, 取低位32bit
  • 另外由于 js 語言的隱式轉換, 對非Number類型使用位運算操作符時會隱式會發生隱式轉換, 相當于先使用Number(xxx)將其轉換為number類型, 再進行位運算:
'str' >>> 0; //  ===> Number('str') >>> 0  ===> NaN >>> 0 = 0

位掩碼

通過位移定義的一組枚舉常量, 可以利用位掩碼的特性, 快速操作這些枚舉產量(增加, 刪除, 比較)

const A = 1 << 0; // 0b00000001
const B = 1 << 1; // 0b00000010
const C = 1 << 2; // 0b00000100
屬性增加|
ABC = A | B | C //0b00000111
屬性刪除& ~
AB = ABC & ~C //0b00000011
屬性比較
AB 當中包含 B: AB & B === B。// AB & B =>0b00000010 ===B,true
AB 當中不包含 C: AB & C === 0 // AB & C =>0b00000000 === 0,true

react中的位運算

  • react在涉及狀態、標記位、優先級操作的地方大量使用了位運算

標記狀態

  • react源碼內部有多個上下文環境,在執行函數時經常需要判斷當前處在哪個上下文環境中
// A上下文
const A = 1; //0001
// B上下文
const B = 2; //0010
// 當前所處上下文
let curContext = 0;
// 沒有處在上下文的標志
const NoContext = 0;
假設進入A的上下文
curContext |= A; //即curContext=curContext|A =>0001

判斷是否處在某一上下文中,結合按位與操作與NoContext
// 是否處在A上下文中,這里為true
(curContext & A) !== NoContext //curContext & A)=>0001 !==0000,所以為true,表示在A的上下文中
// 是否處在B上下文中,這里為false,和上方同理
(curContext & B) !== NoContext 
離開上下文,取出標記進行恢復
// 從當前上下文中移除上下文A
curContext &= ~A; //curContext=curContext& ~A,即0001&1110=0000,進行恢復
// 是否處在A上下文中,此處為false
(curContext & A) !== NoContext //(curContext & A)為0000 

ReactFiberLane.js

  • 優先級定義
  • 源碼中變量只列出了 31 位, 由于 js 中位運算都會轉換成Int32(上文已經解釋), 最多為 32 位, 且最高位是符號位. 所以除去符號位, 最多只有 31 位可以參與運算
//類型定義
export opaque type Lanes = number;
export opaque type Lane = number;
// 變量定義
export const NoLanes: Lanes = /*                        */ 0b0000000000000000000000000000000;
export const NoLane: Lane = /*                          */ 0b0000000000000000000000000000000;
export const SyncLane: Lane = /*                        */ 0b0000000000000000000000000000001;
export const SyncBatchedLane: Lane = /*                 */ 0b0000000000000000000000000000010;
export const InputDiscreteHydrationLane: Lane = /*      */ 0b0000000000000000000000000000100;
const InputDiscreteLanes: Lanes = /*                    */ 0b0000000000000000000000000011000;
const InputContinuousHydrationLane: Lane = /*           */ 0b0000000000000000000000000100000;
const InputContinuousLanes: Lanes = /*                  */ 0b0000000000000000000000011000000;
// ...
// ...
const NonIdleLanes = /*                                 */ 0b0000111111111111111111111111111;
export const IdleHydrationLane: Lane = /*               */ 0b0001000000000000000000000000000;
const IdleLanes: Lanes = /*                             */ 0b0110000000000000000000000000000;
export const OffscreenLane: Lane = /*                   */ 0b1000000000000000000000000000000;

getHighestPriorityLanes

function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes {
  // 判斷 lanes中是否包含 SyncLane
  if ((SyncLane & lanes) !== NoLanes) {
    return_highestLanePriority = SyncLanePriority;
    return SyncLane;
  }
  // 判斷 lanes中是否包含 SyncBatchedLane
  if ((SyncBatchedLane & lanes) !== NoLanes) {
    return_highestLanePriority = SyncBatchedLanePriority;
    return SyncBatchedLane;
  }
  // ...
  // ... 省略其他代碼
  return lanes;
}

getHighestPriorityLane

  • react中處在越低bit位的更新優先級越高(越需要優先處理)
  • 分離出最高優先級
  • -lanes:表示負數的操作,即先取反然后+1
0b000 0000 0000 0000 0000 0000 0001 0001
function getHighestPriorityLane(lanes) {
  return lanes & -lanes;
}
-lanse:
lanes  0001 0001
~lanes 1110 1110 // 第一步
+1     1110 1111 // 第二步
  0001 0001 // lanes  
& 1110 1111 // -lanes
-----------
  0000 0001
若lanes為0001 0000
  0001 0000 // lanes  
& 1111 0000 // -lanes
-----------
  0001 0000

getLowestPriorityLane

  • 假設 lanes(InputDiscreteLanes) = 0b0000000000000000000000000011000
  • 那么 clz32(lanes) = 27, 由于 InputDiscreteLanes 在源碼中被書寫成了 31 位, 雖然在字面上前導 0 是 26 個, 但是轉成標準 32 位后是 27 個
  • index = 31 - clz32(lanes) = 4
  • 最后 1 << index = 0b0000000000000000000000000010000
  • 相比最初的 InputDiscreteLanes, 分離出來了最左邊的1
  • 通過 lanes 的定義, 數字越小的優先級越高, 所以此方法可以獲取最低優先級的 lane
function getLowestPriorityLane(lanes: Lanes): Lane {
  // This finds the most significant non-zero bit.
  const index = 31 - clz32(lanes);
  return index < 0 ? NoLanes : 1 << index;
}

react-reconciler上下文定義

export const NoContext = /*             */ 0b0000000;
const BatchedContext = /*               */ 0b0000001;
const EventContext = /*                 */ 0b0000010;
const DiscreteEventContext = /*         */ 0b0000100;
const LegacyUnbatchedContext = /*       */ 0b0001000;
const RenderContext = /*                */ 0b0010000;
const CommitContext = /*                */ 0b0100000;
export const RetryAfterError = /*       */ 0b1000000;
// ...
// Describes where we are in the React execution stack
let executionContext: ExecutionContext = NoContext;

scheduleUpdateOnFiber

// scheduleUpdateOnFiber函數中包含了好多關于executionContext的判斷(都是使用位運算)
export function scheduleUpdateOnFiber(
  fiber: Fiber,
  lane: Lane,
  eventTime: number,
) {
  if (root === workInProgressRoot) {
    // 判斷: executionContext 不包含 RenderContext
    if (
      deferRenderPhaseUpdateToNextBatch ||
      (executionContext & RenderContext) === NoContext
    ) {
      // ...
    }
  }
  if (lane === SyncLane) {
    if (
      // 判斷: executionContext 包含 LegacyUnbatchedContext
      (executionContext & LegacyUnbatchedContext) !== NoContext &&
      // 判斷: executionContext 不包含 RenderContext或CommitContext
      (executionContext & (RenderContext | CommitContext)) === NoContext
    ) {
      // ...
    }
  }
  // ...
}
  • 在特定的情況下, 使用位運算不僅是提高運算速度, 且位掩碼能簡潔和清晰地表示出二進制變量之間的關系.
  • 但是缺點也很明顯, 不夠直觀, 擴展性不好(在 js 當中的二進制變量, 除去符號位, 最多只能使用 31 位, 當變量的數量超過 31 個就需要組合, 此時就會變得復雜)

總結

原文鏈接:https://blog.csdn.net/weixin_43294560/article/details/123316506

欄目分類
最近更新