返回顶部
首页 > 资讯 > 前端开发 > JavaScript >react 源码中位运算符的使用详解
  • 386
分享到

react 源码中位运算符的使用详解

2024-04-02 19:04:59 386人浏览 薄情痞子
摘要

位运算符基本使用 按位与(&):a & b对于每一个比特位,两个操作数都为 1 时, 结果为 1, 否则为 0按位或(|):a | b对于每一个比特位,两个操作数都为

位运算符基本使用

  • 按位与(&):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 个就需要组合, 此时就会变得复杂)

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注编程网的更多内容! 

--结束END--

本文标题: react 源码中位运算符的使用详解

本文链接: https://lsjlt.com/news/141624.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • react 源码中位运算符的使用详解
    位运算符基本使用 按位与(&):a & b对于每一个比特位,两个操作数都为 1 时, 结果为 1, 否则为 0按位或(|):a | b对于每一个比特位,两个操作数都为...
    99+
    2024-04-02
  • 详细聊聊React源码中的位运算技巧
    目录前言几个常用位运算按位与(&)按位或(|)按位非(~)标记状态优先级计算总结前言 这两年有不少朋友和我吐槽React源码,比如: 调度器为什么用小顶堆这种数据结...
    99+
    2024-04-02
  • C语言详细讲解位运算符的使用
    目录一、位运算符分析二、小贴士三、位运算与逻辑运算四、小结一、位运算符分析 C语言中的位运算符 位运算符直接对 bit 位进行操作,其效率最高。 &按位与|按位或^按位异或~...
    99+
    2024-04-02
  • Java中的位运算与移位运算详解
    目录位运算按位“与” &按位“或” |异或 ^移位运算左移 <<右移 >>无符号右移 >>>总结位运算 按位“与” & ...
    99+
    2024-04-02
  • Java 中的位运算与移位运算详解
    位运算和移位运算是 Java 中常用的运算符,用于对数字的位进行操作。下面是位运算与移位运算的详细解释:1. 位运算符:- 按位与(...
    99+
    2023-08-14
    Java
  • Java中的位运算符全解
    目录1. &(按位与运算符)2. |(按位或运算符)3. ^(异或运算符)4. <<(左移运算符)5. >> (右移移运算符)6. ~(取反运算符)7...
    99+
    2024-04-02
  • 一文详解C++中运算符的使用
    目录一、算术运算符二、关系运算符三、逻辑运算符四、位运算符五、赋值运算符六、杂项运算符一、算术运算符 运算符描述+把两个操作数相加-从第一个操作数中减去第二个操作数*把两个操作数相乘...
    99+
    2024-04-02
  • java中移位运算符怎么使用
    Java中的移位运算符有三种:左移位运算符()和无符号右移位运算符(>>>)。它们用于将一个二进制数向左或向右移动指定的位数。1. ...
    99+
    2023-09-26
    java
  • 详解Java中的运算符
    本篇文章为大家展示了详解Java中的运算符,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。Java的运算符,分为四类:算数运算符、关系运算符、逻辑运算符、位运算符。算数运算符(9):+ - * / %...
    99+
    2023-05-31
    java 运算符 ava
  • 深入解析Python运算符:比较运算符、逻辑运算符、位运算符的用途和含义
    Python运算符解析:比较运算符、逻辑运算符、位运算符的用法和意义 一、比较运算符比较运算符用于比较两个值之间的关系,并返回一个布尔值(True或False)。下面是常见的比较运算符: 等于(==): 判断两个值是否相等,如...
    99+
    2024-01-20
    逻辑 运算符 比较
  • Python3中的算术运算符详解
    目录一·算术运算符二·代码演示1·求和 +2·取差 -3·相乘 *4·相除 /5·取...
    99+
    2024-04-02
  • 详解Java的位运算
    目录位运算代码演示代码结果位运算的应用位运算 很久以前学习过位运算,但是很久不用,感觉都忘得差不多了。最近看了几处位运算的代码,发现都看不懂了,哈。也是时候回来补一补基础知识了。 程...
    99+
    2023-05-15
    Java位运算 Java运算
  • php中怎么使用位运算符中的^和&
    这篇文章给大家分享的是有关php中怎么使用位运算符中的^和&的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。位操作是程序设计中对位模式按位或二进制数的一元和二元操作。在许多古老的微处理器上,位运算比加减运算...
    99+
    2023-06-20
  • C#中的位运算符怎么用
    本文小编为大家详细介绍“C#中的位运算符怎么用”,内容详细,步骤清晰,细节处理妥当,希望这篇“C#中的位运算符怎么用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。实例请看下面的实例,了解 C# 中所有可用的位运算...
    99+
    2023-06-17
  • javascript中instanceof运算符的用法详解
    概述 instanceof运算符用来判断一个构造函数的prototype属性所指向的对象是否存在另外一个要检测对象的原型链上 语法 obj instanceof Object;//t...
    99+
    2024-04-02
  • Python三目运算符(三元运算符)用法详解(含Python代码)
    一、前言 三目运算符,又称条件运算符,是计算机语言(c,c++,java等)的重要组成部分。它是唯一有3个操作数的运算符,有时又称为三元运算符。 定义: 对于条件表达式b x : y,先计算条件b,...
    99+
    2023-09-26
    python 开发语言 三目运算符
  • python移位运算符怎么使用
    Python中的移位运算符包括左移运算符()。移位运算符用于将一个数的二进制位向左或向右移动指定的位数。使用左移运算符(> 2 #...
    99+
    2023-09-21
    python
  • JS中位运算符的一些妙用
    目录前言:1. 使用左移运算符 << 迅速得出2的次方2. 使用 ^ 切换变量 0 或 13. 使用 & 判断奇偶性4. 使用 !! 将数字转为布尔值5. 使用~...
    99+
    2024-04-02
  • c语言中移位运算符如何使用
    C语言中的移位运算符有两种:左移位运算符()。这两种运算符用于对一个数进行位移操作。左移位运算符(>)和算术右移(>>>)。逻辑右移...
    99+
    2023-09-14
    c语言
  • 你可能不知道的JavaScript位运算符详解
    目录概览位操作符概览位操作支持多少位?负数的无符号右移-2 >>> 1为什么输出2147483647状态控制权限控制判断奇偶数交换两个变量的值判断整数是否相等判断是...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作