按位運算符是把操作數看作一系列單獨的位,而不是一個數字值。所以在這之前,不得不提到什么是“位”:
數值或字符在內存內都是被存儲為0和 1的序列,每個0和1被稱之為1個位,比如說10進制數據2在計算機內被存儲為 0 0 0 0 0 0 1 0,當我們將內存內的位值改變之后,這個值代表的意義也就變了,比如把2前移動一位, 現在存儲單元里面變成了0 0 0 0 0 1 0 0,這個值表示的是十進制的4,這也就是按位操作符的運算原理。
按位運算符有6個:
& 按位與
|按位或
^按位異或
~取反
>>右移
<<左移
|按位或
^按位異或
~取反
>>右移
<<左移
1 & 運算符
&是二元運算符,它以特定的方式的方式組合操作數中對應的位 如果對應的位都為1,那么結果就是1, 如果任意一個位是0 則結果就是0
0001
& 0011
---------
0001
判斷一個數是奇數還是偶數,我們會用求余數來判斷:
function assert(n) { if (n % 2 === 1) { console.log("n是奇數"); } else { console.log("n是偶數"); } } assert(3); // "n是奇數"
我們也可以用一個數和1進行按位&操作來判斷,而且速度更快:
function assert(n) { if (n & 1) { console.log("n是奇數"); } else { console.log("n是偶數"); } } assert(3); // "n是奇數"
下面是位運算過程:
1 = 0001
3 = 0011
--------
& = 0001
奇數的二進制碼的最后一位數肯定是1,而1只有最后一位為1,按位&操作之后,結果肯定只有最后一位數為1。而偶數的二進制表示的最后一位數是0,和1進行按位&操作,結果所有位數都為0。
只要任何一位是0 &運算的結果就是 0,所以可以用&把某個變量不必要的位設為0, 比如某個變量的二進制表示為 0 1 0 0 1 0 0 1, 我想保留低4位,消除高4位, 用 & 0x0F就可以了(注:0x為16進制表示法,0x0F 對應的二進制為 0 0 0 0 1 1 1 1)
2 | 運算符
|與||操作符的道理也是一樣的,只要兩個數中有一個數為1,結果就為1,其他則為0。
0001
| 0011
---------
0011
對浮點數向下求整,我們會用下面的方法:
var num = Math.floor(1.1); // 1
我們也可以用位運算來求整:
var num = 1.1 | 0; // 1
其實浮點數是不支持位運算的,所以會先把1.1轉成整數1再進行位運算,就好像是對浮點數向下求整。所以1|0的結果就是1。
3 ^ 運算符
按位異或是兩個數中只有一個1時返回1,其他情況返回0。
0001
^ 0011
---------
0010
數字與數字本身按位異或操作得到的是0,因為每兩個對應的數字都相同,所以最后返回的都是0。
數字與0按位異或操作得到的是數字本身
異或滿足交換律和結合律,類似小學的乘法運算
num1^num2 === num2^num1
num1^num2^num3 === num1^(num2^num3)
證明1:個人聯想, 在二進制中符號位 用0表示正號 用1表示負號 ,十進制用 + - 表示。
用正負號+ -來模擬異或操作, 1:- ,0:+ 。
1^1結果為0, 0^0 結果為^0 , 1^0結果為1, 0^1 結果為1
負負得正 正正的正 負正得負 正負得負
完全十進制符號的運算 故:num1^num2^num3 === num1^(num2^num3)
證明2:數字邏輯法或者說邏輯代數法: 將兩式分別轉化為同一種形式,比如:卡諾圖、真值表、標准與或式等.
(a♁b)♁c
= (a'b + ab')♁c
= (a'b + ab')'c + (a'b + ab')c'
= (a'b)' (ab')' c + a'bc' + ab'c'
= (a + b')(a' + b) c + a'bc' + ab'c'
= abc + a'b'c + a'bc' + ab'c'
a♁(b♁c)
= a'(b♁c) + a(b♁c)'
= a'(b'c + bc') + a(b'c + bc')'
= a'b'c + a'bc' + a(b'c)'(bc')'
= a'b'c + a'bc' + a(b + c')(b' + c)
= a'b'c + a'bc' + abc + ab'c'
顯然,這二者是相等的,證畢.
我們經常會需要調換兩個數字的值:
var num1 = 1, num2 = 2, temp; temp = num1; num1 = num2; // 2
num2 = temp; // 1
如果裝逼一點的話,可以這樣:
var num1 = 1, num2 = 2; num1 = [num2, num2 = num1][0]; console.log(num1); // 2
console.log(num2); // 1
如果想再裝的穩一點的話,可以這樣:
var num1 = 1, num2 = 2; num1 ^= num2; // num1 = num1 ^ num2 = 1 ^ 2 = 3
num2 ^= num1; // num2 = num2 ^ (num1 ^ num2) = num1 = 1
num1 ^= num2; // num1 = num1 ^ num2 ^ num1 = num2 = 2
console.log(num1); // 2
console.log(num2); // 1
4 ~ 運算符
~是對位求反 1變0, 0變1
~4 === -5 , ~-5 === 4
對一個整數num按位求反, 等於它的相反數減一 ~num=-num-1;
~~num == -(num-1)-1 ===num; 對一個數兩次求反結果為這個數本身
浮點數是不支持位運算的,所以會先直接去除小數部分,轉成整數再進行位運算,就好像是對浮點數向下求整
~~可以進行類型轉換,位運算會默認將非數字類型轉換成數字類型再進行運算 (轉換結果為整數 直接去除小數部分)
下面的比較運算結果都為 true
~~true == 1; ~~false == 0; ~~"" == 0; ~~"all" == 0; ~~"32all" == 0; ~~"all43" == 0; ~~[] == 0; ~~undefined ==0; ~~!undefined == 1; ~~null == 0; ~~!null == 1; ~~(5.9) == 5; ~~(5.2) == 5; ~~(-5.9) == -5;
其實按位或運算符 | 0 也有這個特性
5 移位運算符 左移<< , 右移>>
移位運算符把位按指定的值向左或向右移動
<< 向左移動 而 >> 向右移動,超過的位將丟失,而空出的位則補0
如 0 1 0 0 0 0 0 0 0 0 0 0 0 1 1(十進制16387) 向左移動兩位將變成
0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 (十進制12)
向右移動兩位則是
0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 (十進制12)
向右移動兩位則是
0 0 0 1 0 0 0 0 0 0 0 0 0 0 0(十進制4096)
2向前移動1位變成4 利用這個特性可以做乘法運算
2 << 1 = 4
3 << 1 = 6
4 << 1 = 8
3 << 1 = 6
4 << 1 = 8
同理 >> 則可以做除法運算
任何小數 把它 >> 0可以取整
如3.14159 >> 0 = 3;
其默認將非數字類型的轉換為數字類型再做運算的性質與 ~~ , | 0 一樣
位運算有隱式轉換為數字的功能,現在看看js中的一些內置方法將其他類型轉換為數字類型:
1 parseInt(7/2) // 3 丟棄小數部分,保留整數部分 2 Math.ceil(7/2) // 4 向上取整,有小數就整數部分加1 3 Math.round(7/2) // 4 四舍五入. 4 Math.floor(7/2) // 3 向下取整 5 toFixed(n) Number對象的方法--四舍五入保留n位小數。返回 NumberObject 的字符串表示。 var a = 12.3456; document.write(a.toFixed(2)); //12.35
parseInt() 和 parseFloat() 函數會嘗試逐個解析字符串中的字符,直到遇上一個無法被解析成數字的字符,然后返回該字符前所有數字字符組成的數字。如第一個字符不能被解析成為數字,則返回NaN. 使用運算符 "+" 將字符串轉換成數字,只要字符串中含有無法被解析成數字的字符,該字符串都將被轉換成 NaN。
var s = "10.1asd21"; console.log(parseFloat(s)); //10.1 console.log('10ab21'); // 10 parseFloat('a12.21') ; // NaN parseInt('r43') ; // NaN console.log(+s); // NaN
NaN:當算術運算返回一個未定義的或無法表示的值時,NaN就產生了。但是,
NaN並不一定用於表示某些值超出表示范圍的情況。將某些不能強制轉換為數值的非數值轉換為數值的時候,也會得到NaN。
例如,0 除以0會返回
NaN —— 但是其他數除以0則返回Infinity而不是NaN。
與 JavaScript 中其他的值不同,
NaN不能通過相等操作符(== 和 ===)來判斷 ,因為 NaN == NaN 和 NaN === NaN 都會返回 false。 因此,isNaN 就很有必要了。
isNaN(NaN); // true isNaN(undefined); // true isNaN({}); // true isNaN(true); // false isNaN(null); // false isNaN(37); // false // strings isNaN("37"); // false: 可以被轉換成數值37 isNaN("37.37"); // false: 可以被轉換成數值37.37 isNaN(""); // false: 空字符串被轉換成0 isNaN(" "); // false: 包含空格的字符串被轉換成0 // dates isNaN(new Date()); // false isNaN(new Date().toString()); // true isNaN("blabla") // true: "blabla"不能轉換成數值
