导航菜单

  • 0.api
  • 0.Async
  • 0.module
  • 1.ES2015
  • 2.Promise
  • 3.Node
  • 4.NodeInstall
  • 5.REPL
  • 6.NodeCore
  • 7.module&NPM
  • 8.Encoding
  • 9.Buffer
  • 10.fs
  • 11.Stream-1
  • 11.Stream-2
  • 11.Stream-3
  • 11.Stream-4
  • 12-Network-2
  • 12.NetWork-3
  • 12.Network-1
  • 13.tcp
  • 14.http-1
  • 14.http-2
  • 15.compress
  • 16.crypto
  • 17.process
  • 18.yargs
  • 19.cache
  • 20.action
  • 21.https
  • 22.cookie
  • 23.session
  • 24.express-1
  • 24.express-2
  • 24.express-3
  • 24.express-4
  • 25.koa-1
  • 26.webpack-1-basic
  • 26.webpack-2-optimize
  • 26.webpack-3-file
  • 26.webpack-4.tapable
  • 26.webpack-5-AST
  • 26.webpack-6-sources
  • 26.webpack-7-loader
  • 26.webpack-8-plugin
  • 26.webpack-9-hand
  • 26.webpack-10-prepare
  • 28.redux
  • 28.redux-jwt-back
  • 28.redux-jwt-front
  • 29.mongodb-1
  • 29.mongodb-2
  • 29.mongodb-3
  • 29.mongodb-4
  • 29.mongodb-5
  • 29.mongodb-6
  • 30.cms-1-mysql
  • 30.cms-2-mysql
  • 30.cms-3-mysql
  • 30.cms-4-nunjucks
  • 30.cms-5-mock
  • 30.cms-6-egg
  • 30.cms-7-api
  • 30.cms-8-roadhog
  • 30.cms-9-yaml
  • 30.cms-10-umi
  • 30.cms-12-dva
  • 30.cms-13-dva-ant
  • 30.cms-14-front
  • 30.cms-15-deploy
  • 31.dva
  • 31.cms-13-dva-antdesign
  • 33.redis
  • 34.unittest
  • 35.jwt
  • 36.websocket-1
  • 36.websocket-2
  • 38.chat-api-1
  • 38.chat-api-2
  • 38.chat-3
  • 38.chat-api-3
  • 38.chat
  • 38.chat2
  • 38.chat2
  • 39.crawl-0
  • 39.crawl-1
  • 39.crawl-2
  • 40.deploy
  • 41.safe
  • 42.test
  • 43.nginx
  • 44.enzyme
  • 45.docker
  • 46.elastic
  • 47.oauth
  • 48.wxpay
  • index
  • 52.UML
  • 53.design
  • index
  • 54.linux
  • 57.ts
  • 56.react-ssr
  • 58.ts_react
  • 59.ketang
  • 59.ketang2
  • 61.1.devops-linux
  • 61.2.devops-vi
  • 61.3.devops-user
  • 61.4.devops-auth
  • 61.5.devops-shell
  • 61.6.devops-install
  • 61.7.devops-system
  • 61.8.devops-service
  • 61.9.devops-network
  • 61.10.devops-nginx
  • 61.11.devops-docker
  • 61.12.devops-jekins
  • 61.13.devops-groovy
  • 61.14.devops-php
  • 61.15.devops-java
  • 61.16.devops-node
  • 61.17.devops-k8s
  • 62.1.react-basic
  • 62.2.react-state
  • 62.3.react-high
  • 62.4.react-optimize
  • 62.5.react-hooks
  • 62.6.react-immutable
  • 62.7.react-mobx
  • 62.8.react-source
  • 63.1.redux
  • 63.2.redux-middleware
  • 63.3.redux-hooks
  • 63.4.redux-saga
  • 63.5.redux-saga-hand
  • 64.1.router
  • 64.2.router-connected
  • 65.1.typescript
  • 65.2.typescript
  • 65.3.typescript
  • 65.4.antd
  • 65.4.definition
  • 66-1.vue-base
  • 66-2.vue-component
  • 66-3.vue-cli3.0
  • 66-4.$message组件
  • 66-5.Form组件
  • 66-6.tree
  • 66-7.vue-router-apply
  • 66-8.axios-apply
  • 66-9.vuex-apply
  • 66-10.jwt-vue
  • 66-11.vue-ssr
  • 66-12.nuxt-apply
  • 66-13.pwa
  • 66-14.vue单元测试
  • 66-15.权限校验
  • 67-1-network
  • 68-2-wireshark
  • 7.npm2
  • 69-hooks
  • 70-deploy
  • 71-hmr
  • 72.deploy
  • 73.import
  • 74.mobile
  • 75.webpack-1.文件分析
  • 75.webpack-2.loader
  • 75.webpack-3.源码流程
  • 75.webpack-4.tapable
  • 75.webpack-5.prepare
  • 75.webpack-6.resolve
  • 75.webpack-7.loader
  • 75.webpack-8.module
  • 75.webpack-9.chunk
  • 75.webpack-10.asset
  • 75.webpack-11.实现
  • 76.react_optimize
  • 77.ts_ketang_back
  • 77.ts_ketang_front
  • 78.vue-domdiff
  • 79.grammar
  • 80.tree
  • 81.axios
  • 82.1.react
  • 82.2.react-high
  • 82.3.react-router
  • 82.4.redux
  • 82.5.redux_middleware
  • 82.6.connected
  • 82.7.saga
  • 82.8.dva
  • 82.8.dva-source
  • 82.9.roadhog
  • 82.10.umi
  • 82.11.antdesign
  • 82.12.ketang-front
  • 82.12.ketang-back
  • 83.upload
  • 84.graphql
  • 85.antpro
  • 86.1.uml
  • 86.2.design
  • 87.postcss
  • 88.react16-1
  • 89.nextjs
  • 90.react-test
  • 91.react-ts
  • 92.rbac
  • 93.tsnode
  • 94.1.JavaScript
  • 94.2.JavaScript
  • 94.3.MODULE
  • 94.4.EventLoop
  • 94.5.文件上传
  • 94.6.https
  • 94.7. nginx
  • 95.1. react
  • 95.2.react
  • 96.1.react16
  • 96.2.fiber
  • 96.3.fiber
  • 97.serverless
  • 98.websocket
  • 100.1.react-basic
  • 101.1.monitor
  • 101.2.monitor
  • 102.java
  • 103.1.webpack-usage
  • 103.2.webpack-bundle
  • 103.3.webpack-ast
  • 103.4.webpack-flow
  • 103.5.webpack-loader
  • 103.6.webpack-tapable
  • 103.7.webpack-plugin
  • 103.8.webpack-optimize1
  • 103.9.webpack-optimize2
  • 103.10.webpack-hand
  • 103.11.webpack-hmr
  • 103.11.webpack5
  • 103.13.splitChunks
  • 103.14.webpack-sourcemap
  • 103.15.webpack-compiler1
  • 103.15.webpack-compiler2
  • 103.16.rollup.1
  • 103.16.rollup.2
  • 103.16.rollup.3
  • 103.16.vite.basic
  • 103.16.vite.source
  • 103.16.vite.plugin
  • 103.16.vite.1
  • 103.16.vite.2
  • 103.17.polyfill
  • 104.1.binary
  • 104.2.binary
  • 105.skeleton
  • 106.1.react
  • 106.2.react_hooks
  • 106.3.react_router
  • 106.4.redux
  • 106.5.redux_middleware
  • 106.6.connected-react-router
  • 106.6.redux-first-history
  • 106.7.redux-saga
  • 106.8.dva
  • 106.9.umi
  • 106.10.ketang
  • 106.11.antdesign
  • 106.12.antpro
  • 106.13.router-6
  • 106.14.ssr
  • 106.15.nextjs
  • 106.16.1.cms
  • 106.16.2.cms
  • 106.16.3.cms
  • 106.16.4.cms
  • 106.16.mobx
  • 106.17.fomily
  • 107.fiber
  • 108.http
  • 109.1.webpack_usage
  • 109.2.webpack_source
  • 109.3.dll
  • 110.nest.js
  • 111.xstate
  • 112.Form
  • 113.redux-saga
  • 114.react+typescript
  • 115.immer
  • 116.pro5
  • 117.css-loader
  • 118.1.umi-core
  • 119.2.module-federation
  • 119.1.module-federation
  • 120.create-react-app
  • 121.react-scripts
  • 122.react-optimize
  • 123.jsx-runtime
  • 124.next.js
  • 125.1.linux
  • 125.2.linux-vi
  • 125.3.linux-user
  • 125.4.linux-auth
  • 125.5.linux-shell
  • 125.6.linux-install
  • 125.7.linux-system
  • 125.8.linux-service
  • 125.9.linux-network
  • 125.10.nginx
  • 125.11.docker
  • 125.12.ci
  • 125.13.k8s
  • 125.14.k8s
  • 125.15.k8s
  • 125.16.k8s
  • 126.11.react-1
  • 126.12.react-2
  • 126.12.react-3
  • 126.12.react-4
  • 126.12.react-5
  • 126.12.react-6
  • 126.12.react-7
  • 126.12.react-8
  • 127.frontend
  • 128.rollup
  • 129.px2rem-loader
  • 130.health
  • 131.hooks
  • 132.keepalive
  • 133.vue-cli
  • 134.react18
  • 134.2.react18
  • 134.3.react18
  • 135.function
  • 136.toolkit
  • 137.lerna
  • 138.create-vite
  • 139.cli
  • 140.antd
  • 141.react-dnd
  • 142.1.link
  • 143.1.gulp
  • 143.2.stream
  • 143.3.gulp
  • 144.1.closure
  • 144.2.v8
  • 144.3.gc
  • 145.react-router-v6
  • 146.browser
  • 147.lighthouse
  • 148.1.basic
  • 148.2.basic
  • 148.3.basic
  • 148.4.basic
  • 148.5.basic
  • 149.1.vite
  • 149.2.vite
  • 149.3.vite
  • 149.4.vite
  • 150.react-window
  • 151.react-query
  • 152.useRequest
  • 153.transition
  • 154.emotion
  • 155.1.formily
  • 155.2.formily
  • 155.3.formily
  • 155.3.1.mobx.usage
  • 155.3.2.mobx.source
  • 156.vue-loader
  • 103.11.mf
  • 157.1.react18
  • 158.umi4
  • 159.rxjs
  • 159.rxjs2
  • 160.bff
  • 161.zustand
  • 162.vscode
  • 163.emp
  • 164.cors
  • 1.位运算
    • 1.1 比特
    • 1.2 数值数据
      • 1.2.1 无符号数据的表示
      • 1.2.2 有符号数据的表示
        • 1.2.2.1 原码
        • 1.2.2.2 反码
        • 1.2.2.3 补码
    • 1.3 位运算
    • 1.4 使用
    • 1.5 ES5规范
    • 1.6 Hydration
    • 1.7 React中的应用场景
  • 2. 最小堆
    • 2.1 二叉树
    • 2.2 满二叉树
    • 2.3 完全二叉树
    • 2.4 最小堆
    • 2.5 SchedulerMinHeap.js
  • 3. 链表
    • 3.1 链表分类
      • 3.1.1 单向链表
      • 3.1.2 双向链表
      • 3.1.3 循环链表
      • 3.1.4 示例
        • 3.1.4.1 ReactFiberLane.js
        • 3.1.4.2 ReactUpdateQueue.js
        • 3.1.4.3 use.js
  • 4. 树的遍历
    • 4.1 深度优先(DFS)
    • 4.2 广度优先(BFS)
  • 5. 栈
    • 5.1 栈代码
    • 5.2 应用场景
      • 5.2.1 App.js
      • 5.2.2 ReactFiberStack.js
      • 5.2.3 ReactFiberNewContext.js
      • 5.2.4 use.js
  • 6.二进制
    • 6.1 真值
    • 6.2 原码
    • 6.3 反码
    • 6.4 补码
    • 6.5 二进制数整数
    • 6.6 ~非
    • 6.7 getHighestPriorityLane
    • 6.8 左移
    • 6.9 >> 有符号右移
    • 6.10 >>>无符号右移

1.位运算 #

1.1 比特 #

  • 比特(bit)是表示信息的最小单位
  • 比特(bit)是二进制单位(binary unit)的缩写
  • 比特(bit)只有两种状态:0和1
  • 一般来说n比特的信息量可以表示出2的n次方种选择

bit

0b1000=2*2*2=Math.pow(2,3)=8=0~7

1.2 数值数据 #

1.2.1 无符号数据的表示 #

  • 原码: 3个bit能表示8个数 0 1 2 3 4 5 6 7

1.2.2 有符号数据的表示 #

1.2.2.1 原码 #
  • 符号: 用0、1表示正负号,放在数值的最高位,0表示正数,1表示负数
  • 原码: 3个bit能表示8个数 +0 +1 +2 +3 -0 -1 -2 -3

1.2.2.2 反码 #
  • 反码:正数不变,负数的除符号位外取反

1.2.2.3 补码 #
  • 补码:正数不变,负数在反码的基础上加1
  • 补码解决了,可以带符号位进行运算,不需要单独标识
  • 补码解决了自然码正负0的表示方法
  • 补码实现了减法变加法

1.3 位运算 #

  • Binary Bitwise Operators
  • 按位与
  • 按位或
  • 按位异或
  • 按位非
  • 左移
  • 有符号右移
  • 无符号右移
运算 使用 说明
按位与(&) x & y 每一个比特位都为1时,结果为1,否则为0
按位或() x y 每一个比特位都为0时,结果为0,否则为1
按位异或(^) x ^ y 每一个比特位相同结果为0,否则为1
按位非(~) ~ x 对每一个比特位取反,0变为1,1变为0
左移(<<) x << y 将x的每一个比特位左移y位,右侧补充0
有符号右移(>>) x >> y 将x的每一个比特位向右移y个位,右侧移除位丢弃,左侧填充为最高位
无符号右移(>>>) x >>> y 将x的每一个比特位向右移y个位,右侧移除位丢弃,左侧填充为0
let a = 0b100;//4
let b = 0b011;//3
console.log((a & b).toString(2));//000 0
console.log((a | b).toString(2));//111 7 
console.log((a ^ b).toString(2));//111 7 
//按位非运算时,任何数字x的运算结果都是-(x + 1)。例如,〜4运算结果为-5
console.log((~a));//-5
console.log((~a).toString(2));//111
let a = 0b001;
//左移
console.log((a << 2).toString(2));// 0b100

//无符号右移(>>>) 丢弃被移除的位 左侧补0
let b = 0b100;
console.log((b >>> 1).toString(2));// 0b010

//有符号右移(>>) 丢弃被移除的位 左侧填充最高位
let c = 0b101;//-3    110
console.log((-3 >> 1));

1.4 使用 #

//定义常量
const OP_INSERT = 1 << 0; // 0b001
const OP_REMOVE = 1 << 1; // 0b010

let OP = 0b000;
//增加操作
OP |= OP_INSERT;
OP |= OP_REMOVE;
console.log(OP.toString(2));//0b11

//删除操作
OP = OP & ~OP_INSERT;
console.log(OP.toString(2));//0b10

//判断包含
console.log((OP & OP_INSERT) === OP_INSERT);
console.log((OP & OP_REMOVE) === OP_REMOVE);
//判断不包含
console.log((OP & OP_INSERT) === 0);
console.log((OP & OP_REMOVE) === 0);

1.5 ES5规范 #

  • Binary Bitwise Operators
  • ToInt32: (Signed 32 Bit Integer)
  • 位运算只支持整数运算
  • 位运算中的左右操作数都会转换为有符号32位整型, 且返回结果也是有符号32位整型
  • 操作数的大小超过Int32范围(-2^31 ~ 2^31-1). 超过范围的二进制位会被截断, 取低位32bit

1.6 Hydration #

  • 水合反应( hydrated reaction),也叫作水化
  • 是指物质溶解在水里时,与水发生的化学作用,水合分子的过程
  • 组件在服务器端拉取数据(水),并在服务器端首次渲染
  • 脱水: 对组件进行脱水,变成HTML字符串,脱去动态数据,成为风干标本快照
  • 注水:发送到客户端后,重新注入数据(水),重新变成可交互组件




1.7 React中的应用场景 #

  • React中用lane(车道)模型来表示任务优先级
  • 一共有31条优先级,数字越小优先级越高,某些车道的优先级相同
  • clz32)函数返回开头的 0 的个数

//一共有16种优先级
//同步优先级
const SyncLanePriority = 15;
const SyncBatchedLanePriority = 14;
//离散事件优先级
const InputDiscreteHydrationLanePriority = 13;
const InputDiscreteLanePriority = 12;
//连续事件优先级
const InputContinuousHydrationLanePriority = 11;
const InputContinuousLanePriority = 10;
//默认优先级
const DefaultHydrationLanePriority = 9;
const DefaultLanePriority = 8;
//渐变优先级
const TransitionHydrationPriority = 7;
const TransitionPriority = 6;
const RetryLanePriority = 5;
const SelectiveHydrationLanePriority = 4;
//空闲优先级
const IdleHydrationLanePriority = 3;
const IdleLanePriority = 2;
//离屏优先级
const OffscreenLanePriority = 1;
//未设置优先级
const NoLanePriority = 0;
/**
 * 一共有31条车道
 */
const TotalLanes = 31;
//没有车道,所有位都为0
const NoLanes = 0b0000000000000000000000000000000;
const NoLane = 0b0000000000000000000000000000000;
//同步车道,优先级最高
const SyncLane = 0b0000000000000000000000000000001;
const SyncBatchedLane = 0b0000000000000000000000000000010;
//离散用户交互车道 click
const InputDiscreteHydrationLane = 0b0000000000000000000000000000100;
const InputDiscreteLanes = 0b0000000000000000000000000011000;
//连续交互车道 mousemove
const InputContinuousHydrationLane = 0b0000000000000000000000000100000;
const InputContinuousLanes = 0b0000000000000000000000011000000;
//默认车道
const DefaultHydrationLane = 0b0000000000000000000000100000000;
const DefaultLanes = 0b0000000000000000000111000000000;
//渐变车道
const TransitionHydrationLane = 0b0000000000000000001000000000000;
const TransitionLanes = 0b0000000001111111110000000000000;
//重试车道
const RetryLanes = 0b0000011110000000000000000000000;
const SomeRetryLane = 0b0000010000000000000000000000000;
//选择性水合车道
const SelectiveHydrationLane = 0b0000100000000000000000000000000;
//非空闲车道
const NonIdleLanes = 0b0000111111111111111111111111111;
const IdleHydrationLane = 0b0001000000000000000000000000000;
//空闲车道
const IdleLanes = 0b0110000000000000000000000000000;
//离屏车道
const OffscreenLane = 0b1000000000000000000000000000000;

/**
 * 分离出所有比特位中最右边的1
 * 分离出最高优先级的车道
 * @param {*} lanes 车道
 * @returns 车道
 */
function getHighestPriorityLane(lanes) {
    return lanes & -lanes;
}

//console.log(getHighestPriorityLane(InputDiscreteLanes));//8 0b1000
//console.log('0000000000000000000000000011000');
//console.log('1111111111111111111111111101000');

/**
 * 分离出所有比特位中最左边的1
 * 分离出最低优先级的车道
 * @param {*} lanes 车道
 * @returns 车道
 */
function getLowestPriorityLane(lanes) {
    const index = 31 - Math.clz32(lanes);
    return index < 0 ? NoLanes : 1 << index;
}
console.log(getLowestPriorityLane(InputDiscreteLanes));//16 0b10000
console.log('0000000000000000000000000011000');
console.log(Math.clz32(InputDiscreteLanes));//27
console.log(31 - Math.clz32(InputDiscreteLanes));//4

2. 最小堆 #

2.1 二叉树 #

  • 每个节点最多有两个子节点

2.2 满二叉树 #

  • 除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树

2.3 完全二叉树 #

  • 叶子结点只能出现在最下层和次下层
  • 且最下层的叶子结点集中在树的左部

2.4 最小堆 #

  • processon
  • 最小堆是一种经过排序的完全二叉树
  • 其中任一非终端节点的数据值均不大于其左子节点和右子节点的值
  • 根结点值是所有堆结点值中最小者
  • 编号关系
    • 左子节点编号=父节点编号2 12=2
    • 右子节点编号=左子节点编号+1
    • 父节点编号=子节点编号/2 2/2=1
  • 索引关系
    • 左子节点索引=(父节点索引+1)2-1 (0+1)2-1=1
    • 右子节点索引=左子节点索引+1
    • 父节点索引=(子节点索引-1)/2 (1-1)/2=0
  • Unsigned_right_shift

2.5 SchedulerMinHeap.js #

  • peek() 查看堆的顶点
  • pop() 弹出堆的定点后需要调用siftDown函数向下调整堆
  • push() 添加新节点后需要调用siftUp函数向上调整堆
  • siftDown() 向下调整堆结构, 保证最小堆
  • siftUp() 需要向上调整堆结构, 保证最小堆

react\packages\scheduler\src\SchedulerMinHeap.js

export function push(heap, node) {
  const index = heap.length;
  heap.push(node);
  siftUp(heap, node, index);
}
export function peek(heap) {
  const first = heap[0];
  return first === undefined ? null : first;
}
export function pop(heap) {
  const first = heap[0];
  if (first !== undefined) {
    const last = heap.pop();
    if (last !== first) {
      heap[0] = last;
      siftDown(heap, last, 0);
    }
    return first;
  } else {
    return null;
  }
}

function siftUp(heap, node, i) {
  let index = i;
  while (true) {
    const parentIndex = index - 1 >>> 1;
    const parent = heap[parentIndex];
    if (parent !== undefined && compare(parent, node) > 0) {
      heap[parentIndex] = node;
      heap[index] = parent;
      index = parentIndex;
    } else {

      return;
    }
  }
}

function siftDown(heap, node, i) {
  let index = i;
  const length = heap.length;
  while (index < length) {
    const leftIndex = (index + 1) * 2 - 1;
    const left = heap[leftIndex];
    const rightIndex = leftIndex + 1;
    const right = heap[rightIndex]; 
    if (left !== undefined && compare(left, node) < 0) {
      if (right !== undefined && compare(right, left) < 0) {
        heap[index] = right;
        heap[rightIndex] = node;
        index = rightIndex;
      } else {
        heap[index] = left;
        heap[leftIndex] = node;
        index = leftIndex;
      }
    } else if (right !== undefined && compare(right, node) < 0) {
      heap[index] = right;
      heap[rightIndex] = node;
      index = rightIndex;
    } else {
      return;
    }
  }
}

function compare(a, b) {
  const diff = a.sortIndex - b.sortIndex;
  return diff !== 0 ? diff : a.id - b.id;
}
const { push, pop, peek } = require('./SchedulerMinHeap');
let heap = [];
push(heap, { sortIndex: 1 });
push(heap, { sortIndex: 2 });
push(heap, { sortIndex: 3 });
console.log(peek(heap));
push(heap, { sortIndex: 4 });
push(heap, { sortIndex: 5 });
push(heap, { sortIndex: 6 });
push(heap, { sortIndex: 7 });
console.log(peek(heap));
pop(heap);
console.log(peek(heap));

3. 链表 #

  • 链表是一种物理存储单元上非连续、非顺序的存储结构
  • 数据元素的逻辑顺序是通过链表中的指针链接次序实现的
  • 链表由一系列结点组成
  • 每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域

3.1 链表分类 #

3.1.1 单向链表 #

3.1.2 双向链表 #

3.1.3 循环链表 #

3.1.4 示例 #

3.1.4.1 ReactFiberLane.js #

ReactFiberLane.js

const NoLanes = 0b00;
const NoLane = 0b00;
const SyncLane = 0b01;
const SyncBatchedLane = 0b10;
/**
 * 判断subset是不是set的子集
 * @param {*} set 
 * @param {*} subset 
 * @returns 
 */
function isSubsetOfLanes(set, subset) {
    return (set & subset) === subset;
}
/**
 * 合并两个车道
 * @param {*} a 
 * @param {*} b 
 * @returns 
 */
function mergeLanes(a, b) {
    return a | b;
}
module.exports = {
    NoLane,
    NoLanes,
    SyncLane,
    SyncBatchedLane,
    isSubsetOfLanes,
    mergeLanes
}
3.1.4.2 ReactUpdateQueue.js #

ReactUpdateQueue.js

const { NoLane, NoLanes, isSubsetOfLanes, mergeLanes } = require('./ReactFiberLane') ;
function initializeUpdateQueue(fiber) { 
    const queue = {
        baseState: fiber.memoizedState,//本次更新前该Fiber节点的state,Update基于该state计算更新后的state
        firstBaseUpdate: null,//本次更新前该Fiber节点已保存的Update链表头
        lastBaseUpdate: null,//本次更新前该Fiber节点已保存的Update链表尾
        shared: {
            //触发更新时,产生的Update会保存在shared.pending中形成单向环状链表
            //当由Update计算state时这个环会被剪开并连接在lastBaseUpdate后面
            pending: null
        }
    }
    fiber.updateQueue = queue;
}
function enqueueUpdate(fiber, update) {
    const updateQueue = fiber.updateQueue;
    if (updateQueue === null) {
        return;
    }
    const sharedQueue = updateQueue.shared;
    const pending = sharedQueue.pending;
    if (pending === null) {
        update.next = update;
    } else {
        update.next = pending.next;
        pending.next = update;
    }
    sharedQueue.pending = update;
}

/**
 * 处理更新队列
 * @param {*} fiber 
 * @param {*} renderLanes 
 */
function processUpdateQueue(fiber, renderLanes) {
    //获取此fiber上的更新队列
    const queue = fiber.updateQueue;
    //获取第一个更新
    let firstBaseUpdate = queue.firstBaseUpdate;
    let lastBaseUpdate = queue.lastBaseUpdate; 
    //判断一下是否在等待生效的的更新,如果有,变成base队列
    let pendingQueue = queue.shared.pending;
    if (pendingQueue !== null) {
        //等待生效的队列是循环队列
        queue.shared.pending = null; 
        //最后一个等待的更新 update4
        const lastPendingUpdate = pendingQueue;
        //第一个等待的更新 update1
        const firstPendingUpdate = lastPendingUpdate.next;
        //把环剪断,最后一个不再指向第一个
        lastPendingUpdate.next = null;
        //把等待生效的队列添加到base队列中
        //如果base队列为空
        if (lastBaseUpdate === null) {
            firstBaseUpdate = firstPendingUpdate;
        } else {//否则就把当前的更新队列添加到base队列的尾部
            lastBaseUpdate.next = firstPendingUpdate;
        }
        //尾部也接上
        lastBaseUpdate = lastPendingUpdate;
    } 
    //开始计算新的状态
    if (firstBaseUpdate !== null) {
        //先获取老的值{number:0}
        let newState = queue.baseState;
        let newLanes = NoLanes;
        let newBaseState = null;//新的基础状态
        let newFirstBaseUpdate = null;//第一个跳过的更新
        let newLastBaseUpdate = null;//新的最后一个基本更新
        let update = firstBaseUpdate;//指向第一个更新
        do {
            //获取更新车道
            const updateLane = update.lane;
            //如果优先级不够,跳过这个更新,如果这是第一个跳过的更新,上一个状态和更新成为newBaseState和newFirstBaseUpdate
            if (!isSubsetOfLanes(renderLanes, updateLane)) {
                const clone = {
                    lane: updateLane,
                    payload: update.payload
                };
                if (newLastBaseUpdate === null) {
                    newFirstBaseUpdate = newLastBaseUpdate = clone;// first=last=update1
                    newBaseState = newState;//0
                } else {
                    newLastBaseUpdate = newLastBaseUpdate.next = clone;
                }
                //更新队列中的剩下的优先级
                newLanes = mergeLanes(newLanes, updateLane);
            } else {
                //如果有足够的优先级 如果有曾经跳过的更新,仍然追加在后面
                if (newLastBaseUpdate !== null) {
                    const clone = {
                        //NoLane是所有的优先级的子集,永远不会被跳过
                        lane: NoLane,
                        payload: update.payload
                    };
                    newLastBaseUpdate = newLastBaseUpdate.next = clone;
                }
                newState = getStateFromUpdate(update, newState);
            }
            update = update.next;
            if (!update) {
                break;
            }
        } while (true);

        if (!newLastBaseUpdate) {
            newBaseState = newState;
        }
        queue.baseState = newBaseState;
        queue.firstBaseUpdate = newFirstBaseUpdate;
        queue.lastBaseUpdate = newLastBaseUpdate;
        fiber.lanes = newLanes;
        fiber.memoizedState = newState;
    }
}

function getStateFromUpdate(update, prevState) {
    const payload = update.payload;
    let partialState = payload(prevState);
    return Object.assign({}, prevState, partialState);
}
module.exports = {
    initializeUpdateQueue,
    enqueueUpdate,
    processUpdateQueue
}
3.1.4.3 use.js #

use.js

4. 树的遍历 #

4.1 深度优先(DFS) #

  • 深度优先搜索英文缩写为DFS即Depth First Search
  • 其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次
  • 应用场景
    • React虚拟DOM的构建
    • React的fiber树构建
function dfs(node) {
    console.log(node.name);
    node.children&&node.children.forEach(child => {
        dfs(child);
    });
}
let root = {
    name: 'A',
    children: [
        {
            name: 'B',
            children: [
                { name: 'B1' },
                { name: 'B2' }
            ]
        },
        {
            name: 'C',
            children: [
                { name: 'C1' },
                { name: 'C2' }
            ]
        }
    ]
}
dfs(root);

4.2 广度优先(BFS) #

  • 宽度优先搜索算法(又称广度优先搜索),其英文全称是Breadth First Search
  • 算法首先搜索距离为k的所有顶点,然后再去搜索距离为k+l的其他顶点
function bfs(node) {
    const stack = [];
    stack.push(node);
    let current;
    while (current = stack.shift()) {
        console.log(current.name);
        current.children && current.children.forEach(child => {
            stack.push(child);
        });
    }
}
let root = {
    name: 'A',
    children: [
        {
            name: 'B',
            children: [
                { name: 'B1' },
                { name: 'B2' }
            ]
        },
        {
            name: 'C',
            children: [
                { name: 'C1' },
                { name: 'C2' }
            ]
        }
    ]
}
bfs(root);

5. 栈 #

  • 栈(stack)又名堆栈,它是一种运算受限的线性表
  • 限定仅在表尾进行插入和删除操作的线性表,这一端被称为栈顶,相对地,把另一端称为栈底
  • 向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素
  • 从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素

5.1 栈代码 #

class Stack {
    constructor() {
        this.data = [];
        this.top = 0;
    }
    push(node) {
        this.data[this.top++] = node;
    }
    pop() {
        return this.data[--this.top];
    }
    peek() {
        return this.data[this.top - 1];
    }
    size() {
        return this.top;
    }
    clear() {
        this.top = 0;
    }
}

const stack = new Stack();
stack.push('1');
stack.push('2');
stack.push('3');
console.log('stack.size()', stack.size());
console.log('stack.peek', stack.peek());
console.log('stack.pop()', stack.pop());
console.log('stack.peek()', stack.peek());
stack.push('4');
console.log('stack.peek', stack.peek());
stack.clear();
console.log('stack.size', stack.size());
stack.push('5');
console.log('stack.peek', stack.peek());

5.2 应用场景 #

5.2.1 App.js #

import React from "react";
const NumberContext = React.createContext(0);
export default function App() {
    return (
        <NumberContext.Provider value={'A'}>
            <NumberContext.Consumer>
                {(v1) => (
                    <NumberContext.Provider value={'B'}>
                        <NumberContext.Consumer>
                            {(v2) => (
                                <NumberContext.Provider value={'C'}>
                                    <NumberContext.Consumer>
                                        {(v3) => (
                                            <div>
                                                {v1} {v2} {v3}
                                            </div>
                                        )}
                                    </NumberContext.Consumer>
                                </NumberContext.Provider>
                            )}
                        </NumberContext.Consumer>
                    </NumberContext.Provider>
                )}
            </NumberContext.Consumer>
        </NumberContext.Provider>
    );
}

5.2.2 ReactFiberStack.js #

src\react\packages\react-reconciler\src\ReactFiberStack.js

const valueStack = [];
let index = -1;
function createCursor(defaultValue) {
    return {current: defaultValue};
}

function isEmpty() {
    return index === -1;
}

function pop(cursor) {
    if (index < 0) {
        return;
    }
    cursor.current = valueStack[index];
    valueStack[index] = null;
    index--;
}

function push(cursor, value) {
    index++;
    valueStack[index] = cursor.current;
    cursor.current = value;
}

module.exports =  {
    createCursor, isEmpty, pop, push
};

5.2.3 ReactFiberNewContext.js #

src\react\packages\react-reconciler\src\ReactFiberNewContext.js

function pushProvider(providerFiber, nextValue) {
    const context = providerFiber.type._context;
    push(valueCursor, context._currentValue, providerFiber);
    context._currentValue = nextValue;
}
function popProvider(providerFiber) {
    const currentValue = valueCursor.current;
    pop(valueCursor, providerFiber);
    const context = providerFiber.type._context;
    context._currentValue = currentValue;
}
module.exports =  {
    pushProvider, popProvider
};

5.2.4 use.js #

const { createCursor, pop, push } = require('./ReactFiberStack.js');
const { pushProvider, popProvider } = require('./ReactFiberNewContext.js');
let valueCursor = createCursor();
let providerFiber = { type: { _context: {} } };
pushProvider(providerFiber, 'A');
console.log(providerFiber.type._context._currentValue);
pushProvider(providerFiber, 'B');
console.log(providerFiber.type._context._currentValue);
pushProvider(providerFiber, 'C');
console.log(providerFiber.type._context._currentValue);
popProvider(providerFiber);
console.log(providerFiber.type._context._currentValue);
popProvider(providerFiber);
console.log(providerFiber.type._context._currentValue);
popProvider(providerFiber);

6.二进制 #

  • 计算机用二进制来存储数字
  • 为了简化运算,二进制数都是用一个字节(8个二进制位)来简化说明
  • 在线工具

6.1 真值 #

  • 8位二进制数能表示的真值范围是[-2^8, +2^8]
+ 00000001 # +1
- 00000001 # -1

6.2 原码 #

  • 由于计算机只能存储0和1,不能存储正负
  • 所以用8个二进制位的最高位来表示符号,0表示正,1表示负,用后七位来表示真值的绝对值
  • 这种表示方法称为原码表示法,简称原码
  • 由于10000000的意思是-0,这个没有意义,所有这个数字被用来表示-128
  • 由于最高位被用来表示符号了,现在能表示的范围是[-2^7, +2^7-1],即[-128, +127]
0 0000001 # +1
1 0000001 # -1

6.3 反码 #

  • 反码是另一种表示数字的方法
  • 其规则是整数的反码何其原码一样
  • 负数的反码将其原码的符号位不变,其余各位按位取反
  • 反码的表示范围是[-2^7, +2^7-1],即[-128, +127]
0 0000001 # +1
1 1111110 # -1

6.4 补码 #

  • 补码是为了简化运算,将减法变为加法而发明的数字表示法
  • 其规则是整数的补码和原码一样,负数的补码是其反码末尾加1
  • 8位补码表示的范围是[-2^7, +2^7-1],即[-128, +127]
  • 快速计算负数补码的规则就是,由其原码低位向高位找到第一个1,1和其低位不变,1前面的高位按位取反即可
0 0000001 # +1
1 1111111 # -1

6.5 二进制数整数 #

  • 只要对js中的任何数字做位运算操作系统内部都会将其转换成整形
  • js中的这种整形是区分正负数的
  • js中的整数的表示范围是[-2^31, +2^31-1],即[-2147483648, +2147483647]

6.6 ~非 #

  • ~操作符会将操作数的每一位取反,如果是1则变为0,如果是0则边为1
0b00000011
3
~0b00000011 =>  0b11111100
-4
(~0b00000011).toString();
'-4'
(~0b00000011).toString(2);
'-100'

求补码的真值
1 表示负号
剩下的 1111100 开始转换
1111100 减1
1111011 取反
0000100 4

6.7 getHighestPriorityLane #

  • 可以找到最右边的1
  • 最右边的1右边的全是0,全是0取反就全是1,再加上就会全部进位到1取反的位置
  • 最右边的1和右边的数跟原来的值是完全一样的,左边的全是反的
/**
 * 分离出所有比特位中最右边的1
 * 分离出最高优先级的车道
 * @param {*} lanes 车道
 * @returns 车道
 */
function getHighestPriorityLane(lanes) {
    return lanes & -lanes;
}

lanes=0b00001100=12
-lanes=-12
1
0001100
1110011
1110100
11110100
00001100

6.8 左移 #

  • 左移的规则就是每一位都向左移动一位,末尾补0,其效果相当于×2
  • 计算机就是用移位操作来计算乘法的
(0b00000010<<1).toString(2)

6.9 >> 有符号右移 #

  • 有符号右移也就是移位的时候高位补的是其符号位,整数则补0,负数则补1
(-0b111>>1).toString(2) "-100"
-0b111 -7
100000111 原码
111111000 反码
111111001 补码
111111100

1
111111100
111111011
000000100
1000000100
-100
-4

6.10 >>>无符号右移 #

  • 右移的时候高位始终补0
  • 整数和有符号右移没有区别
  • 负数右移后会变为正数
(0b111>>>1).toString(2)
>>> "11"
(-0b111>>>1).toString(2)
>>> "1111111111111111111111111111100"

00000000000000000000000000000111
11111111111111111111111111111000
11111111111111111111111111111001
01111111111111111111111111111100
2147483644

访问验证

请输入访问令牌

Token不正确,请重新输入