在前端中,二进制码似乎在平常的开发中很少会去使用到,但是在一些框架中,也会看到其作为开发的一部分存在,并且结合位运算,有时也能达到较好的使用效果。
// react\packages\shared\ReactSideEffectTags.js 中使用的相关状态码
export type SideEffectTag = number;
// Don't change these two values. They're used by React Dev Tools.
export const NoEffect = /* */ 0b00000000000;
export const PerformedWork = /* */ 0b00000000001;
// You can change the rest (and add more).
export const Placement = /* */ 0b00000000010;
export const Update = /* */ 0b00000000100;
export const PlacementAndUpdate = /* */ 0b00000000110;
export const Deletion = /* */ 0b00000001000;
export const ContentReset = /* */ 0b00000010000;
export const Callback = /* */ 0b00000100000;
export const DidCapture = /* */ 0b00001000000;
export const Ref = /* */ 0b00010000000;
export const Snapshot = /* */ 0b00100000000;
简单的位运算
位运算不会进位,不要与二进制的加减运算混淆
与(AND) 或(OR) 异或(XOR) 非(NOT)
0b001 0b001 0b011
& 0b010 | 0b010 ^ 0b001 ~ 0b011
-------- -------- -------- --------
0b000 0b011 0b010 0b100
在js中的数字类型转换中:
0,NaN对应的boolean值为false123,-123,Infinity,...对应的boolean值为true。
所以利用这一特性可以将二进制数位运算的值转换为boolean值。
运用位运算实例
吃饭,睡觉,打豆豆为例
// 存在的各种状态(为了对齐,借鉴React的书写方式)
const Eat = /* */ 0b001 // 吃饭
const Sleep = /* */ 0b010 // 睡觉
const Fight = /* */ 0b100 // 打豆豆
const Mode = /* */ 0b000 // 用户的初始状态
let eatAndSleep = Mode | Eat | Sleep // 0b011
let emptyMode = Mode // 0b000
let fightMode = Mode | Fight // 0b100
// 检验是否吃饭
console.log(eatAndSleep & Eat) // 1 (0b001) -> true
console.log(emptyMode & Eat) // 0 (0b000) -> false
console.log(fightMode & Eat) // 0 (0b000) -> false
// 检验是否打豆豆
console.log(eatAndSleep & Fight) // 0 (0b000) -> false
console.log(emptyMode & Fight) // 0 (0b000) -> false
console.log(fightMode & Fight) // 4 (0b100) -> true
添加类型
let mode = Mode | Eat // 增加吃饭的标记 0b001
是否有类型
let hasEat = Mode & Eat // 结果0或者非0
// let hasEat = !!(Mode & Eat) // 结果为true/false
去除类型
mode = mode ^ Eat // 去除睡觉的标记 0b000
mode = mode & ~(Eat) // 去除睡觉的标记
mode = mode & ~(Eat | Sleep) // 去除 睡觉,吃饭 的标记
更为通用的方法
/**
* 判断是否存在当前的状态
* @param {Number} currentMode 当前的状态
* @param {Number} targetMode 目标状态
* @return {Boolean} 是否存在
*/
function hasMode(currentMode, targetMode) {
return (currentMode & targetMode) !== 0
}
/**
* 为当前状态码添加状态
* @param {Number} currentMode 当前的状态
* @param {Number} targetMode 目标状态
* @return {Number} 新生成的状态吗
*/
function addMode(currentMode, targetMode) {
return currentMode | targetMode
}
/**
* 为当前状态码去除状态
* @param {Number} currentMode 当前的状态
* @param {Number} targetMode 目标状态
* @return {Number} 新生成的状态吗
*/
function removeMode(currentMode, targetMode) {
// return currentMode & ~targetMode // 也可使用
return currentMode ^ targetMode // 可使用 ^异或去除
}
使用方式
使用封装的方法
let eatMode = addMode(Mode, Eat)
let eatAndSleepMode = addMode(eatMode, Sleep)
console.log(hasMode(eatAndSleepMode, Eat)) // true
let sleepMode = removeMode(eatAndSleepMode, Eat)
console.log(hasMode(sleepMode, Eat)) // false
直接使用二进制操作
React源码中大量使用到了位操作
!!操作可以直接转换为boolean值
let eatAndSleepMode = Mode | Eat | Sleep
console.log(!!(eatAndSleepMode & Eat)) // true
let sleepMode = eatAndSleepMode & ~Eat
console.log(!!(sleepMode & Eat)) // false
有时React会直接操作当前mode
effectTag |= Ref // 将Ref标识添加到effectTag中
effectTag &= ~Placement // 将Placement状态从effectTag中去除