位运算

MDN:位运算

JS对数字的存储是以IEEE-754 64位格式进行存储的,但是位运算并不会直接操作64位的值,而是先将64位转成32位进行计算,最后在将结果转换为64位。 如果转成2进制不足32位,则会转成先转成32位进行计算,不足的部分用0进行填充。如果超过则丢弃不要,在计算机中,最左边为高位,最右边为低位

本篇为了方便了解,全部采用32位进行标记

语法例子描述
按位与 ANDa & b在a,b的位表示中,每一个对应的位都为1则返回1, 否则返回0
按位或 ORa | b在a,b的位表示中,每一个对应的位,只要有一个为1则返回1, 否则返回0
按位异或 XORa ^ b在a,b的位表示中,每一个对应的位,两个不相同则返回1,相同则返回0
按位非 NOT~a对每位进行取反
左移a << b

将a的二进制串向左移动b位,低位空缺补0,高位溢出不要

左移不考虑正负数

右移a >> b

把a的二进制表示向右移动b位

正数 右移的时候,最高位补0,低位舍去

负数 右移的时候,最高位补1,低位舍去

无符号右移

(左边空出位用0填充)

a >>> b把a的二进制表示向右移动b位,溢出舍去,高位补0(不处理正负数,高位不0)

按位与 AND

  • 每一位相同为1,不同为0
例子二进制结果/对应的二进制
5 & 5

0000 0000 0000 0000 0000 0000 0000 0101

0000 0000 0000 0000 0000 0000 0000 0101

5

0000 0000 0000 0000 0000 0000 0000 0101

5 & 10

0000 0000 0000 0000 0000 0000 0000 0101

0000 0000 0000 0000 0000 0000 0000 1010

5

0000 0000 0000 0000 0000 0000 0000 0000

按位或 OR

  • 只要每个对应的位有1个是1就为1,否则为0
    例子二进制结果/对应的二进制
    10 | 22

    0000 0000 0000 0000 0000 0000 0000 1010

    0000 0000 0000 0000 0000 0000 0001 0110

    30

    0000 0000 0000 0000 0000 0000 0001 1110

按位异或 XOR

  • 每个对应的位,不同为1,相同为0
    例子二进制结果/对应的二进制
    10 ^ 22

    0000 0000 0000 0000 0000 0000 0000 1010

    0000 0000 0000 0000 0000 0000 0001 0110

    28

    0000 0000 0000 0000 0000 0000 0001 1100

按位非 NOT

  • 对每个位进行取反

注意:

  • 在进行按位非运算时,所有数字的计算结果都是~(x + 1)
  • 由于对数字~-1~4294967295 (232-1) 使用32位表示形式,结果均为0。

例子二进制结果/对应的二进制
~10

0000 0000 0000 0000 0000 0000 0000 1011

-11

0000 0000 0000 0000 0000 0000 0000 0100

左移

  • 向左移动n位,低位补0,高位不要
例子二进制结果/对应的二进制
2 << 2

2

0000 0000 0000 0000 0000 0000 0000 0010

8

0000 0000 0000 0000 0000 0000 0000 1000

右移

  • 向右移动n位
例子二进制结果/对应的二进制
10 >> 2

10

0000 0000 0000 0000 0000 0000 0000 1010

2

0000 0000 0000 0000 0000 0000 0000 0010

无符号右移

  • 把a的二进制表示向右移动b位,溢出舍去,高位补0(不处理正负数,高位不0
例子二进制结果/对应的二进制
54 >>> 2

54

0000 0000 0000 0000 0000 0000 0011 0110

13

0000 0000 0000 0000 0000 0000 0000 1101

vue-next对位运算的应用

vue-next中,充满了大量对位运算的操作,主要是针对一些内容做的标记,如:

  • patchFlags 编译优化相关
  • shapFlags 标记当前元素类型
  • slotFlags slot相关

patchFlags

代码在packages/shared/src/patchFlags.ts

export const enum PatchFlags {
  TEXT = 1, // 动态文本
  CLASS = 1 << 1, // 2 动态class
  STYLE = 1 << 2, // 4 动态style
  PROPS = 1 << 3, // 8
  FULL_PROPS = 1 << 4, // 16
  HYDRATE_EVENTS = 1 << 5, // 32
  STABLE_FRAGMENT = 1 << 6, // 64
  KEYED_FRAGMENT = 1 << 7, // 128
  UNKEYED_FRAGMENT = 1 << 8, // 256
  NEED_PATCH = 1 << 9, // 512
  DYNAMIC_SLOTS = 1 << 10, // 1024
  DEV_ROOT_FRAGMENT = 1 << 11, // 2048
  HOISTED = -1,
  BAIL = -2
}

shapFlags

代码在packages/shared/src/shapeFlags.ts

export const enum ShapeFlags {
  ELEMENT = 1,
  FUNCTIONAL_COMPONENT = 1 << 1, // 2
  STATEFUL_COMPONENT = 1 << 2, // 4
  TEXT_CHILDREN = 1 << 3, // 8
  ARRAY_CHILDREN = 1 << 4, // 16
  SLOTS_CHILDREN = 1 << 5, // 32
  TELEPORT = 1 << 6, // 64
  SUSPENSE = 1 << 7, // 128
  COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8, // 256
  COMPONENT_KEPT_ALIVE = 1 << 9, // 512
  COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT // 4 | 2
}

slotFlags

代码在packages/shared/src/slotFlags.ts

export const enum SlotFlags {
  /**
   * Stable slots that only reference slot props or context state. The slot
   * can fully capture its own dependencies so when passed down the parent won't
   * need to force the child to update.
   */
  STABLE = 1,
  /**
   * Slots that reference scope variables (v-for or an outer slot prop), or
   * has conditional structure (v-if, v-for). The parent will need to force
   * the child to update because the slot does not fully capture its dependencies.
   */
  DYNAMIC = 2,
  /**
   * `<slot/>` being forwarded into a child component. Whether the parent needs
   * to update the child is dependent on what kind of slots the parent itself
   * received. This has to be refined at runtime, when the child's vnode
   * is being created (in `normalizeChildren`)
   */
  FORWARDED = 3
}