JavaScript 的位元運算符一共有7個,分別是&、|、^、~、>、>>>(C#沒有這個運算符,但是C#可以透過>>的邏輯右移來實現此運算),位元運算的操作都是透過二進位進行的。
按位與運算符(&)
當兩個數相同位都為1時返回1,否則返回0,例如1&2=0,1的二進位表示為0001,2的二進位表示為0010,二者的運算則回傳0000。
位元或運算子(|)
當兩個數相同位數字不同的時候返回1,否則返回0,例如1|2=3。
位元異或運算子(^)
當兩個數相同位元僅有一個為1的時候返回1,否則返回0,例如1^2=3。
位元非運算子(~)
~是一個一元運算符,它將所有位數取反。這裡首先必須要說下負數的存儲,負數是以其正數的二進制補碼進行存儲的,所以我們在進行負數的運算時,必須要正確的獲取其二進制編碼,也就是其正數的二進制補碼。補碼是取反然後加1來實現,下面看範例:
先計算3的反碼:3的二進位形式為00000011,其反碼為11111100,其補碼為11111101,所以-3的二進位編碼為11111101,那麼我們要求~-3,就是取其反碼,為00000010,這就是-3的反碼,將其轉換為十進位為2。
多試幾個就會發現,其實一個數的反碼就是其十進制的相反數減去1。
左移運算子(
左移運算子就是將數的所有位元集體左移,第一位變成第二位,第二位變成第三位。 。 。空出的新位用0補充。例如1
這裡我們可以發現左移運算就是將其十進位數乘以2的位數次方。
帶符號的右移運算子(>>)
既然左移是乘以2,那麼右移肯定應該是除以2了,事實上就是這樣子的,如果數字本身為正數,則在高位補0,若為負數則在高位補1。例如3>>1=1,-3>>1=-2。3的二進位編碼表示為0000 0011,將其向右移動1位,得到0000 0001,轉換為十進位就是1,-3的二進位編碼為1111 1101,將其向右移動1位得到1111 1110,這是負數,負數轉換為十進制,先減一得到1111 1101,取反為0000 0010,得到-2。
帶符號的右移運算就是將其十進制數除以2的位數次方,並捨棄餘數。
無符號的右移運算子(>>>)
正數的無符號右移運算結果跟帶符號的右移運算是一樣的,主要是負數的無符號右移運算。它跟帶符號的右移的區別就在於,不管是正數還是負數,高位都以0補充,所以對正數來說帶符號和無符號的運算都是一樣的,而對於負數來說則是天壤之別。例如:-1>>>1=2147483647,數字很恐怖是吧,看看計算過程:-1的二進位編碼為1111 1111 1111 1111 1111 1111 1111 1111,將其右移1位,並補11011 1111 1111 1111 1111 1111 1111,第一位為0,是正數,將其轉換為十進位就是230+229+……+20=230(1-1/231)/(1-1/2)=231- 1=2147483647,這樣最終得到了我們需要的結果,結果很恐怖,慎用!
位運算符的應用:
談了這麼久,最終的目的還是為了去用這些運算符,看些例子:
顏色的RGB值和十六進制的轉換:例如一個顏色值:# 33cc10,前兩位代表紅色(R),中間兩位代表綠色(G),後兩位代表藍色(B),將其轉換為二進位編碼為:0011 0011 1100 1100 0001 0000(賦給color),首先我們要取得紅色值,需要將其右移16位,color>>16,也就是0000 0000 0000 0000 0011 0011,這樣子我們取得到R=51,那麼我們要取得綠色值就需要將其先右移8位,color>>8,得到0000 0000 0011 0011 1100 1100,然後將前八位變為0,0000 0000 0011 0011 1100 1100|0000 000010 00 0000 1100 1100,這樣子我們得到G=204,最後取藍色值,就是簡單的將其前八位變為0,color | 0000 0000 0000 0000 0001 0000,我們得到B=16,#33cc10轉化為RGB值就是(51,204,16 )。反過來RGB轉換成十六進位正好是反過來的方法,就是G
判斷一個節點是否為另一個節點的父節點:例如有兩個節點a和b,ie的方法是a.contains(b)來決定a是否為b的子節點,而其它現代瀏覽器所使用的方法是a.compareDocumentPosition(b),這個回傳結果並不是一個boolean值,如果a和b是同一個節點則回傳0,a和b在不同的document內或至少有一個在document之外則回傳1,如果b在a之前則回傳2,a在b之前則回傳4,b包含a則回傳8,a包含b則回傳16,32則為瀏覽器獨享。 0、1、2、4、8、16的二進位編碼分別為0000 0000、0000 0001、0000 0010、0000 0100、0000 1000、0001 0000,我們可以透過透過核對.compareDomentP對true還是false來判斷a是否為b的節點,那為什麼不用a.compareDocumentPosition(b) == 16判斷呢?因為a.compareDocumentPosition(b)回傳的應該是20(4+ 16),所以倒可以用a.compareDocumentPosition(b) == 20來運算,用&運算子的好處在於我們不需要考慮這些,我們只需要考慮它和我們需要的值16的&運算是否可以回傳true。 (John Resig有一個模擬compareDocumentPosition的方法,讓其在ie下同樣適用,有興趣的可以參考文末的連結~)
按位左移運算:我們知道按位左移1位,就是乘以2,那我們可以用a
位元右移:一方面,可以用a>>1取代a/2,另外位元右移可以方便的將小數轉換為整數,如3.1415>>0=3,因為以位移運算會將運算數字必須為整數(詳細請參考ECMA-262 手冊),所以操作後將捨棄小數位~
注:位運算符要求它的數字運算數是整數的,並且這些運算數是用32位的整數來表示的,第32位是符號位。而且運算數限制在32位元的整數範圍內,同時要求右邊的運算數在0到31之間。 (本文二進位編碼並不規範,僅為方便使用~)