js種使用 >> | & ~四種操作符做標(biāo)志(status|Flag)管理

什么是標(biāo)志?標(biāo)志就是顯示事物特征,便于識(shí)別的記號(hào)。在程序中我們通常使用枚舉來表示標(biāo)志,比如權(quán)限標(biāo)志:

// authorityFlag.js

// 是否有查詢權(quán)限
const QUERY = 1
// 是否有更新權(quán)限
const UPDATE = 2
// 是否有刪除權(quán)限
const DELETE = 3
// 是否有新增權(quán)限
const INSERT = 4

以往我們通常是使用數(shù)組來這這類標(biāo)志管理的

// 用戶擁有的權(quán)限
const userAuths = [QUERY, UPDATE, INSERT]

// 是否包含權(quán)限
const containFlag = (auths, [...flags]) => 
  flags.every(flag => auths.includes(flag))

// 是否沒有權(quán)限
const noContainFlag = (auths, [...flags]) =>
  flags.every(flag => !auths.includes(flag))

// 是否只有某個(gè)權(quán)限
const equalFlag = (auths, flag) =>
  flags.length === 1 && flags[0] === flag

// 添加權(quán)限
const addFlag = (auths, [...flags]) => {
  flags.forEach(flag => {
    if (!containFlag(flag)) {
      auths.push(flag)
    }
  })
}

// 刪除權(quán)限
const removeFlag = (auths, [...flags]) => {
  flags.forEach(flag => {
    const index = flags.indexOf(flag)
    if (index > -1) {
      flags.splice(index, 1)
    }
  })
}

現(xiàn)在我給大家分享使用左移(>>)、按位或(|)、按位與(&)、按位或(|)按位非(~)四種操作符結(jié)合做標(biāo)志管理的方法

左移(>>)

左移操作符 (<<) 將第一個(gè)操作數(shù)向左移動(dòng)指定位數(shù),左邊超出的位數(shù)將會(huì)被清除,右邊將會(huì)補(bǔ)零。比如:

const numToBinaryString = 
  num => console.log(num.toString(2))

numToBinaryString(5);       // 00000000000000000000000000000101
numToBinaryString(5 << 2);  // 00000000000000000000000000010100
numToBinaryString(5 << 3);  // 00000000000000000000000000101000

按位或(|)

按位或運(yùn)算符 (|) 其中一個(gè)操作數(shù)或兩個(gè)都是 1,則這個(gè)位上返回 1 ??梢钥醋髅總€(gè)操作數(shù)的||操作符比較好理解,比如:

numToBinaryString(5);       // 00000000000000000000000000000101
numToBinaryString(3);       // 00000000000000000000000000000011
numToBinaryString(5 | 3);   // 00000000000000000000000000000111

按位與(&)

按位與運(yùn)算符 (&) 兩個(gè)操作數(shù)對(duì)應(yīng)的位都是 1,則這個(gè)位上返回 1 ??梢钥醋髅總€(gè)操作數(shù)的&&操作符比較好理解,比如:

numToBinaryString(5);       // 00000000000000000000000000000101
numToBinaryString(3);       // 00000000000000000000000000000011
numToBinaryString(5 & 3);   // 00000000000000000000000000000001

按位非 (~)

按位非運(yùn)算符(~),反轉(zhuǎn)操作數(shù)的位??梢钥醋魉形粩?shù)1 0 反轉(zhuǎn),比如:

numToBinaryString(5);       // 00000000000000000000000000000101
numToBinaryString(~5);      // 11111111111111111111111111111010

使用 >>、 |、 &、 ~ 做標(biāo)志管理

因?yàn)槿齻€(gè)運(yùn)算符的特殊關(guān)系,所以我們需要在二進(jìn)制數(shù)中,每個(gè)狀態(tài)都只有一個(gè)位數(shù)是1,且不能相同,如下

// before
const QUERY = 1
const UPDATE = 2
const DELETE = 3
const INSERT = 4

// after                
const QUERY = 1         // 00000000000000000000000000000001
const UPDATE = 1 << 2   // 00000000000000000000000000000010
const DELETE = 1 << 3   // 00000000000000000000000000000100
const INSERT = 1 << 4   // 00000000000000000000000000001000

那是如何管理這些標(biāo)志的呢,相信你已經(jīng)聰明的你猜到了,

拼合多個(gè)標(biāo)志

因?yàn)?按位或(|) 的特性,我們可以將擁有的標(biāo)志的位數(shù)都聯(lián)合起來

// before
const userAuths = [QUERY, UPDATE, INSERT]

//after
const userAuths = QUERY | UPDATE | INSERT   // 00000000000000000000000000001011

是否包含

因?yàn)?按位與(&) 的特性,我們可以單獨(dú)判斷是否擁有某個(gè)標(biāo)志,因?yàn)橹挥袃蓚€(gè)位數(shù)都為1才會(huì)為1,只要結(jié)果不為0即為存在

// before
const containFlag = (auths, [...flags]) => 
  flags.every(flag => auths.includes(flag))

//after
const containFlag = (auths, [...flags]) =>
  flags.every(flag => auths & flag)
/**
 * 比如 auths & QUERY運(yùn)算結(jié)果為
 *  00000000000000000000000000001011
 *  00000000000000000000000000000001
 * =00000000000000000000000000000001
 * =1
 * 比如 auths & DELETE運(yùn)算結(jié)果為
 *  00000000000000000000000000001011
 *  00000000000000000000000000000100
 * =00000000000000000000000000000000
 * =0
 * 直接使用更加方便,比如判斷是否有刪除和更新權(quán)限
 * if (auths & UPDATE && auths & DELETE)) {
 *  ...
 * }
 */

是否不包含某個(gè)標(biāo)志

// before
const containFlag = (auths, [...flags]) => 
  flags.every(flag => auths.includes(flag))

//after
const containFlag = (auths, [...flags]) =>
  flags.every(flag => !(auths & flag))

判斷是否只有某個(gè)標(biāo)志

這個(gè)跟平時(shí)一樣,只要使用全等操作符就可以了

// before
const equalFlag = (auths, flag) =>
  flags.length === 1 && flags[0] === flag

//after
// 判斷是否只有某個(gè)標(biāo)志直接使用全等即可
const equalFlag = (auths, flag) => auths === flag
/* 直接使用更方便
 * if (auths === UPDATE) {
 *  ...
 * }
 */
 

添加標(biāo)志

其實(shí)添加標(biāo)志與初始化一樣,只需要使用 按位或(|)直接聯(lián)合即可,而且不需要判斷當(dāng)前是否有,因?yàn)榧尤胗械脑捠褂?code>按位或(|)會(huì)保持跟之前一致

// before
const addFlag = (auths, [...flags]) => {
  flags.forEach(flag => {
    if (!containFlag(flag)) {
      auths.push(flag)
    }
  })
}

// after
const addFlag = (auths, [...flags]) => {
  flags.forEach(flag => {
    auths |= flag
  })
}
/**
 * 比如 auths | DELETE運(yùn)算結(jié)果為
 *  00000000000000000000000000001011
 *  00000000000000000000000000000100
 * =00000000000000000000000000001111
 * 直接使用更加方便,要添加權(quán)限,z
 * auths = auths | DELETE | QUERY
 */

刪除標(biāo)志

刪除標(biāo)志本質(zhì)上是要將要?jiǎng)h除標(biāo)志中1的位數(shù)在變成0,這有些麻煩,因?yàn)闊o論是按位或(|)或者 按位與(&) 都不能滿足,按位或(|)在有這個(gè)標(biāo)志時(shí)不變,在不存在這個(gè)標(biāo)志時(shí)會(huì)添加,按位與(&)也不行,在沒有這個(gè)標(biāo)志時(shí)會(huì)全部清除,在有這個(gè)標(biāo)志時(shí)會(huì)僅剩這個(gè)標(biāo)志.這時(shí)候我們就要引入上文說的按位非 (~)運(yùn)算符了,我們可以先將要?jiǎng)h除的標(biāo)志取按位非,這時(shí)候除了表示標(biāo)志的位數(shù)為0其他都為1,這時(shí)候我們按位與(&)聯(lián)合這個(gè)處理后的數(shù)字,就能得到我們想要的效果了

// before
const removeFlag = (auths, [...flags]) => {
  flags.forEach(flag => {
    const index = auths.indexOf(flag)
    if (index > -1) {
      flags.splice(index, 1)
    }
  })
}
// after
const removeFlag = (auths, [...flags]) => {
  flags.forEach(flag => {
    auths &= ~flag
  })
}

/**
 * 比如 auths &= ~DELETE運(yùn)算結(jié)果為
 *  UPDATE            00000000000000000000000000000010
 *  ~UPDATE           11111111111111111111111111111101
 *  auths             00000000000000000000000000001011
 *  auths &= ~UPDATE  00000000000000000000000000001001
 * 直接使用更加方便,要?jiǎng)h除的權(quán)限,
 * auths & ~DELETE
 */

優(yōu)缺點(diǎn)

優(yōu)點(diǎn):相對(duì)于數(shù)組來說使用位運(yùn)算符的速度要快得多,特別是狀態(tài)比較多,或者使用比較頻繁的情況下,而且對(duì)于熟悉位運(yùn)算符的人來說,更加直觀.

缺點(diǎn):正如優(yōu)點(diǎn)所對(duì)應(yīng)的一樣,對(duì)于不熟悉位運(yùn)算符的開發(fā)同學(xué),這一定是魔鬼寫法,非常不友好.而且左移(>>)運(yùn)算符最多只支持32位左移,比如numToBinaryString(1 << 30)結(jié)果就到達(dá) 1000000000000000000000000000000了,所以這種管理標(biāo)志方法最多只能支持31中標(biāo)志,不過已經(jīng)足夠了,因?yàn)榇蟛糠謶?yīng)用是不超過31種標(biāo)志的.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 今日早讀文章由騰訊@CoyPan翻譯授權(quán)分享。 正文從這開始~~ JavaScript提供了幾種運(yùn)算符,可以...
    videring閱讀 481評(píng)論 0 0
  • 一元操作符 只能操作一個(gè)值的操作符。 遞增和遞減操作符 前置型:位于要操作的變量之前; 后置型:位于要操作的變量之...
    Sketch閱讀 392評(píng)論 0 0
  • 平時(shí)很少用到按位操作符,總是記不清楚,特此記錄一下: 運(yùn)算符用法描述按位與( AND)a & b對(duì)于每一個(gè)比特位,...
    砂壺閱讀 153評(píng)論 0 0
  • 1、概述 除了一些基本的操作符,Swift提供了幾種能夠執(zhí)行更加復(fù)雜值操縱的高級(jí)操作符。其中包括你所熟悉的C和OC...
    本人莫等閱讀 767評(píng)論 0 0
  • 按位操作符(Bitwise operators) 將其操作數(shù)(operands)當(dāng)作32位的比特序列(由0和1組成...
    靦腆的童木哥閱讀 992評(píng)論 0 1

友情鏈接更多精彩內(nèi)容