巧用JS位運算

2021-09-11 08:57:42 字數 4320 閱讀 5062

位運算的方法在其它語言也是一樣的,不侷限於js,所以本文提到的位運算也適用於其它語言。

位運算是低階的運算操作,所以速度往往也是最快的(相對其它運算如加減乘除來說),並且借助位運算的特性還能實現一些演算法。恰當地使用運算有很多好處。下面舉幾個例子。

這是乙個很常用的技巧,如判斷乙個數是否在陣列裡面:

// 如果url含有?號,則後面拼上&符號,否則加上?號

url += ~url.indexof("?") ? "&" : "?";複製**

因為:

~-1 === 0

-1在記憶體的表示的二進位制符號全為1,按位非之後就變成了0. 進一步說明——1在記憶體的表示為: 0000...0001,第一位0表示符號位為正,如果是-1的話符號位為負用1表示1000...0001,這個是-1的原碼,然後符號位不動,其餘位取反變成1111...1110,這個就是-1的反碼表示,反碼再加1就變成了1111...1111,這個就是-1的補碼,負數在記憶體裡面(機器數)使用補碼表示,正數是用原碼。所以全部都是1的機器數按位非之後就變成了全為0。剩下的其它所有數按位非都不為0,所以利用這個特性可以用來做indexof的判斷,這樣**看起來更簡潔一點。

交換兩個整數的值,最直觀的做法是借助乙個臨時變數:

let a = 5,

b = 6;

// 交換a, b的值

let c = a;

a = b;

b = c;複製**

現在要求不能使用額外的變數或內容空間來交換兩個整數的值。這個時候就得借助位運算,使用異或可以達到這個目的:

let a = 5,

b = 6;

a = a ^ b;

b = a ^ b; // b 等於 5

a = a ^ b; // a 等於 6

複製**

這個是為什麼呢?很簡單,把1、2式:

a = a ^ b;

b = a ^ b;複製**

連起來就等價於:

b = (a ^ b) ^ b = a ^ (b ^ b) = a ^ 0 = a;複製**
同理連同第3式可得:

a = (a ^ b) ^ a  // 在執行第3式的時候b已經變成a了,而a是第1式的a ^ b

= a ^ a ^ b = 0 ^ b = b;複製**

為什麼a ^ a = 0, b ^ b = 0呢?因為異或的運算就是這樣的。如下示例:

01011010

^ 10010110

-----------

11001100複製**

異或的運算過程可以當作把兩個數加起來,然後進製去掉,0 + 0 = 0,1 + 0 = 1,1 + 1 = 0。這樣就很好記。所以a ^ a在所有二進位制位上,要麼同為0,要麼同為1,相加結果都為0,最後就為0.

異或還經常被用於加密。

按位與有很多作用,其中乙個就是去運算元的高位,只保留低位,例如有a, b兩個數:

let a = 0b01000110; // 十進位制為70

let b = 0b10000101; // 十進位制為133

複製**

現在認為他們的高位是沒用的,只有低4位是有用的,即最後面4位,為了比較a,b後4位的大小,可以這樣比較:

a & 0b00001111

< b & 0b00001111

// true

複製**

a, b的前4位和0000與一下之後就都變成0了,而後四位和1111與一下之後還是原來的數。這個實際的作用是有乙個數字它的前幾位被當作a用途,而後幾位被用當b用途,為了去掉前幾位對b用途的影響,就可以這樣與一下。

另外乙個例子是子網掩碼,假設現在我是網路管理員,我能夠管理的ip位址是從192.168.1.0到192.168.1.255,即只能調配最後面8位。現在把這些ip位址分成6個子網,通過ip位址進行區分,由於6等於二進位制的110,所以最後面8位的前3位用來表示子網,而後5位用來表示主機(即總的主機數的範圍為00001 ~ 11111, 共30個)。當前網路的子網掩碼取為255.255.255.11100000即255.255.255.224,假設某台主機的ip位址為192.168.1.120,現在要知道它處於哪個子網,可以用它ip位址與子網掩碼與一下:120 & 224 = 96 = 0b01100000,就知道它所在的子網為011即3號子網。

這個是保留高位去掉低位的例子,剛好與上面的例子相反。

現在有個後台管理系統,操作許可權分為一級、二級、**管理員,其中一級管理員擁有最高的許可權,二、**較低,有些操作只允許

一、二級管理員操作,有些操作只允許

一、**管理員操作。現在已經登陸的某許可權的使用者要進行某個操作,要用怎樣的資料結構能很方便地判斷他能不能進行這個操作呢?

我們用位來表示管理許可權,一級用第3位,二級用第2位,**用第1位,即一級的許可權表示為0b100 = 4,二級許可權表示為0b010 = 2,**許可權表示為0b001 = 1。如果a操作只能由一級和二級操作,那麼這個許可權值表示為6 = 0b110,它和一級許可權與一下:6 & 4 = 0b110 & 0b100 = 4,得到的值不為0,所以認為有許可權,同理和二級許可權與一下6 & 2 = 2也不為0,而與**許可權與一下6 & 1 = 0,所以**沒有許可權。這裡標誌位的1表示開啟,0表示關閉。

這樣的好處在於,我們可以用乙個數字,而不是乙個陣列來表示某個操作的許可權集,同時在進行許可權判斷的時候也很方便。

上面構造了乙個許可權的屬性集,屬性集的例子還有很多,例如我在《google地圖開發總結》裡面就提到乙個邊界判斷的例子——要在當前滑鼠的位置往上彈乙個懸浮框,如下圖左所示,但是當滑鼠比較靠邊的時候就會導致懸浮框超出邊界了,如下圖右所示:

為此,需要做邊界判斷,總共有3種超出情況:右、上、左,並且可能會疊加,如滑鼠在左上角的時候會導致左邊和上面同時超出。需要記錄超出的情況進行調整,用001表示右邊超出,010表示上方超出,100表示左邊超出,如下**計算:

let postflag = 0;

//右邊超出

if(pos.right < maxlen) posflag |= 1;

//上面超出

if(pos.top < maxlen) posflag |= 2;

//左邊超出

if(pos.left < maxleftlen) posflag |= 4;

//對超出的情況進行處理,**略

switch(posflag)複製**

如果左邊和上面同時超出,那麼通過或運算2 | 4 = 6,得到6 = 0b110. 就知道了超出的情況,這樣的**相對於在if裡面寫兩個判斷要好一些。

這裡有個例子——不使用加減乘除來做加法,經常用來考察對位運算的掌握情況。讀者可以先自行嘗試分析和實現。

不能用加減乘除,意思就是要你用位運算進行計算。以實際例子說明,如a = 81 = 0b1010001,b = 53 = 0b0110101。通過異或運算,我們發現異或把兩個數相加但是不能進製,而通過與運算能夠知道哪些位需要進製,如下所示:

1010001

^ 0110101

---------

1100100

1010001

& 0110101

---------

0010001複製**

把通過與運算得到的值向左移一位,再和通過異或得到的值相加,就相當於實現了進製,這個應該不難理解。為了實現這兩個數的相加可以再重複這個過程:先異或,再與,然後進製,直到不需要再進製了就加完了。所以不難寫出以下**:

function

addbybit(a, b)

// 不用進製的相加

let c = a ^ b;

// 記錄需要進製的

let d = a & b;

d = d << 1;

// 繼續相加,直到d進製為0

return addbybit(c, d);

}let ans = addbybit(5, 8);

console.log(ans);複製**

位運算還經常用於生成隨機數、雜湊,例如chrome對字串進行雜湊的演算法是這樣的:

uint32_t stringhasher::addcharactercore(uint32_t running_hash, uint16_t c) 複製**
不斷對當前字串的ascii值進行累加運算,裡面用到了異或,左移和右移。

本篇介紹了使用位運算的幾個實際的例子,希望能加深位運算的理解、對開發有所幫助。

巧用JS位運算

1.使用按位非 判斷索引存在 這是乙個很常用的技巧,如判斷乙個數是否在陣列裡面 如果url含有?號,則後面拼上 符號,否則加上?號 url url indexof 因為 1 0 1在記憶體的表示的二進位制符號全為1,按位非之後就變成了0.進一步說明 1在記憶體的表示為 0000.0001,第一位0表...

巧用位運算

1 用乙個表示式,判斷乙個數x是否是2的n次方 2,4,8,16.不可用迴圈語句。解析 x 2,4,8,16轉化成二進位制是10,100,1000,10000。如果減1則變成01,011,0111,01111。兩者做按位與運算,結果如果為0,則x是2的n次方。答案 x x 1 2 統計乙個整數的二進...

巧用位運算

1 不用額外空間交換兩個數字變數的值 解1 異或運算 只能交換整數 var a 3 var b 4 a a b a 7 b a b b 3 a a b a 4解2 加減法運算 非整數存在精度問題 var a 3 var b 4 a a b a 7 b a b b 3 a a b a 4解3 利用乘除...