在vue源码目录下的packages/shared模块下定义了一堆公共变量和方法,其中有几个文件pathFlags和shapeFlags内部返回了两个枚举,枚举并不稀奇,比较有意思的是这内部的写法。
这两个文件的枚举值都是按位左移1个位。如下所示
export const enum ShapeFlags {
ELEMENT = 1,
FUNCTIONAL_COMPONENT = 1 << 1,
STATEFUL_COMPONENT = 1 << 2,
TEXT_CHILDREN = 1 << 3,
ARRAY_CHILDREN = 1 << 4,
SLOTS_CHILDREN = 1 << 5,
TELEPORT = 1 << 6,
SUSPENSE = 1 << 7,
COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8,
COMPONENT_KEPT_ALIVE = 1 << 9,
COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT
}
export const enum PatchFlags {
TEXT = 1,
CLASS = 1 << 1,
STYLE = 1 << 2,
PROPS = 1 << 3,
FULL_PROPS = 1 << 4,
HYDRATE_EVENTS = 1 << 5,
STABLE_FRAGMENT = 1 << 6,
KEYED_FRAGMENT = 1 << 7,
UNKEYED_FRAGMENT = 1 << 8,
NEED_PATCH = 1 << 9,
DYNAMIC_SLOTS = 1 << 10,
DEV_ROOT_FRAGMENT = 1 << 11,
HOISTED = -1,
BAIL = -2
}
上述两段代码本质上是一个东西,这种表示方式通过位移分别设置不同的状态位,我们将其转化为二进制再看看 代码如下
for (i=0;i<33;i++) {
let num = (1 << i).toString(2).padStart('32',0)
let idx = (i).toString().padStart(2,'0')
console.log(`%s %s`,idx, num)
}
00 00000000000000000000000000000001
01 00000000000000000000000000000010
02 00000000000000000000000000000100
03 00000000000000000000000000001000
04 00000000000000000000000000010000
05 00000000000000000000000000100000
06 00000000000000000000000001000000
07 00000000000000000000000010000000
08 00000000000000000000000100000000
09 00000000000000000000001000000000
10 00000000000000000000010000000000
11 00000000000000000000100000000000
12 00000000000000000001000000000000
13 00000000000000000010000000000000
14 00000000000000000100000000000000
15 00000000000000001000000000000000
16 00000000000000010000000000000000
17 00000000000000100000000000000000
18 00000000000001000000000000000000
19 00000000000010000000000000000000
20 00000000000100000000000000000000
21 00000000001000000000000000000000
22 00000000010000000000000000000000
23 00000000100000000000000000000000
24 00000001000000000000000000000000
25 00000010000000000000000000000000
26 00000100000000000000000000000000
27 00001000000000000000000000000000
28 00010000000000000000000000000000
29 00100000000000000000000000000000
30 01000000000000000000000000000000
31 -10000000000000000000000000000000
32 00000000000000000000000000000001
这里我们循环了33次,很有意思的现象就来了,那就是在第32次的时候整个数值编程了负数,而第33次的时候有重新回归起始点了。
在计算机世界中,任何事物都只是1和0,数值型也不例外,数值只不过是计算机为了让我们看得懂而做的人机界面转换,它只认识0和1,也正是如此,计算机如果相区分正数负数也只能依靠1和0。而如何确定这些呢,还得依靠标准和规范,而大多数语言都是遵守ILP32或ILP64的整型规范。
我们的弱类型语言也不例外,毕竟是基于CPP的,在C/CPP中一个整形通常使用四个字节表示,这也解释了为什么我们输出的位数是32位而非其它位数。
其次为啥位移到31的时候数值的正负就发生变化了呢?
那是因为在整型变量中还分为unsigned 和 signed 即无符号整型和有符号整型。区分他们的就看高位是否作为正负状态位,因为计算机不像人,一眼分正负。所以通过统一的规范,大家都这么做,那么计算机就这么设计,自然就能分出正负了,这也是标准的强大。
那么也就是说上面的状态位写法,其实最多支持32种,如果太多可能普通的数值类型就不满足要求了,希望vue组件的状态不要这么多吧。
补充,数据位移动到顶部的时候,下一次就出现在了起始位置,这种现象有个转移名词,叫做循环右移
那么这种写法的好处呢,我们知道一般一个变量只能标识一个值,而如何想通过一个变量标识多种同时存在的状态,这种写法就非常有用了
如一个节点包含了 class,style和props三个状态值,那么我们就会得到如下的值status,而获取这样的值,最直接的方法就是通过位或来进行运算
CLASS = 1 << 1,
STYLE = 1 << 2,
PROPS = 1 << 3
CLASS 00000000000000000000000000000010
STYLE 00000000000000000000000000000100
PROPS 00000000000000000000000000001000
STATUS 00000000000000000000000000001110
位运算一般包括
| 名称 | 解释 | 示例 |
|---|---|---|
| 与 | 同一为1 | 1&1=1 1&0=0 0&0=0 |
| 或 | 有一为一 | 1|0=1 1|1=1 0|0=0 |
| 非 | 取反 | !1=0 !0=1 |
| 同或 | 同值为1 | 1^1=1 1^0=0 0^0=1 |
| 异或 | 异值为1 | 1^1=0 1^0=1 0^0=0 |