位操做及其應用

我在看lodash實現一些工具函數的源碼時發現lodash定義了一些bitMask的常量。我一開始沒弄明這是什麼鬼東西,用Google搜了一圈才發現是我以前接觸過得位操做運算一類的東西。而且源碼和我搜索的資料給我提供了另外一種使用場景,感受應用性仍是蠻強的,因此乾脆總結一下好了。算法

先如下面的表達式展開須要瞭解的基礎知識。數組

// lodash 源碼裏定義的常量
var CLONE_DEEP_FLAG = 1

JavaScript遵循 IEEE 754 標準,不管是整數仍是小數都是用雙精度浮點數表述,雙精度浮點數8個字節,表示64位二進制位,因此雙精度浮點數的表示範圍是-2^63 ~ 2^63-1。可是在進行位操做時則是用的32位數表示,也就是4個字節,表示範圍爲`-2^31 ~ 2^31-1,其中不管是32位仍是64位,最高位都是符號位,0表示正數,1表示負數。函數

上面的表達式在進行位操做就會轉換成下面這種,若是超過32位了,那超過的部分就會所有省去。工具

00000000 00000000 00000000 00000001

下面介紹幾種經常使用的操做符。加密

&(位與)

let a = 1,
    b = 2
    
console.log(a & b) // 0

// 0001
// 0010
// = 0000

把變量a和變量b都展開成32位二進制數,省去前面的0,a的二進制表示爲0001b0010,接着就是對應位數的二進制位比較,若是相同就是1,不然爲0。code

|(位或)

let a = 1,
    b = 2
    
console.log(a | b) // 3

// 0001
// 0010
// = 0011

參照上面一種,不一樣的是相同的二進制位上,只要有一個是1,則結果就是1,因此就是0011ip

^(位異或)

這個和|有點區別,相同的地方在於若是同一位數上的數只要一個是1,則這個位數的結果就是1,不一樣的地方在於相同的位數上若是數值相同,則結果爲0.字符串

let a = 1,
    b = 2
    
console.log(a ^ b) // 3

// 0001
// 0010
// = 0011

let c = 3,
    d = 3
    
console.log(a ^ b) // 0

// 0011
// 0011
// = 0000

~(位異或)

這個和以前三個最大的區別是對單個數的操做,而不是兩個數的比較結果。簡單來講就是取反,對二進制上的每一位都取反。可是這裏有個有趣的現象。源碼

let a = 1;

console.log(~ a) // -2

不管用~取反任何數,得出的都是負數,並且是在正數上加一的負數。這裏涉及三個概念:原碼反碼補碼。首先明確一點,負數是以補碼的形式存在的it

原碼

正數和負數的都是轉換成二進制數後的樣子,不一樣的是負數的原碼在最高位+1。

// 4的原碼
00000000 000000000 000000000 00000100
// -4的原碼
10000000 000000000 000000000 00000100

反碼

正數的反碼和正數的原碼一致。但負數的反碼是對除了符號位上的其餘二進制位取反。

// 4的反碼
00000000 000000000 000000000 00000100
// -4的反碼
11111111 11111111 11111111 11111011

補碼

正數的補碼仍是和正數的原碼一致,但負數的補碼是在負數反碼的基礎上對最後一位加1。

// 4的補碼
00000000 000000000 000000000 00000100
// -4的補碼
11111111 11111111 11111111 11111100

因此說回上面提到的問題,3被~轉換爲-4的過程。

3:00000000 00000000 00000000 00000100
~3:11111111 11111111 11111111 11111011

// ~3 這時候表示的是負數,那就按照原碼->補碼的順序倒推 
// 1.最低的位數-1
 11111111 11111111 11111111 11111010 
// 2.取反(除了符號位)
10000000 00000000 00000000 00000101
// 3.轉換成十進制
-4

應用

介紹完了幾種操做符,來講說有啥應用。

136. 只出現一次的數字

這是LeetCode上的一道題,題目是這樣寫的:

給定一個非空整數數組,除了某個元素只出現一次之外,其他每一個元素均出現兩次。找出那個只出現了一次的元素。

這個用上面介紹過的^(異或)操做符是最容易解答的,能夠能夠下^的特性。題目裏說的是隻有一個數是惟一的,其餘都是兩兩出現,而相同的數字,就像我上面舉例用了兩個3,結果是0,由於每一個二進制上的數都相同,因此結果就是每一個位上都變成了0.

function onlyNums(arr){
    return arr.reduce((all,item) => all ^ item)
}

權限

這個例子就和咱們的平常貼的比較近了。後臺系統進行權限配置的時候,通常可能就是定義幾個字符串定義不一樣的權限,若是一我的同時有不少權限,結果多是個數組,也多是把不一樣權限字符串拼接成新的字符串。

let permission1 = 001 // 登陸權限
let permission2 = 002 // 建立權限
let permission3 = 003 // 刪除權限
let adminPermission = '001,002,003'
// or
let adminPermission = [001,002,003]

若是換成位操做的思路:

let permission1 = 1 // 登陸權限
let permission2 = 2 // 建立權限
let permission3 = 4 // 刪除權限
let permission4 = 8 // 編輯權限

let adminPermission = (permission1 | permission2 | permission3)

這個時候檢查adminPermission是否擁有某個權限就能夠這樣:

if((adminPermission & permission1) === permission1){
    // 有登陸權限
}

刪除某個權限:

adminPermission = adminPermission & (~ permission2)

新增某個權限:

adminPermission = adminPermission | permission4

除了上面兩種場景,位操做還用於加密算法中,可是個人工做沒有涉及過這方面,因此就不具體展開說了。感興趣的話能夠本身看看。

相關文章
相關標籤/搜索