## 運算符
ECMA-262描述了一組用于操作數據值的運算符,包括算術運算符、位運算符、關系運算符和相等運算符。
**1.1 一元運算符**
只能操作一個值的運算符叫做一元運算符。
**1.遞增和遞減**
遞增(++)和遞減(--)都有兩種使用方式:**前置型**和**后置型**。
**前置型**指的是運算符放在要操作的變量之前,而**后置型**則是運算符放在要操作的變量之后。
```
var age = 10;
var age2 = 10;
++age;
--age;
console.log(age); // 11
console.log(age2); //9
```
執行前置遞增和遞減操作時,變量的值都是在語句被求值以前改變的(也就是先自增或自減,再和其他值進行操作):
```
var num = 2;
var age = ++num + 2;
console.log(age); // 5
```
在上面的例子中,在與2相加之前,num已經加1變成了2。
后置型遞增和遞減運算符不變(依然是++和--),不過除了要放到變量之后外,包含它們的語句是被求值之后才執行(先和其他值進行操作后,再自增或自減):
```
var num = 2;
var age = (num++) + 2;
console.log(age); // 4
console.log(num); // 3
```
注意:由于JavaScript會自動進行分號補全,因此不能在后增量運算符和操作數之間插入換行符
```
num
++;
```
這會報錯
這4個操作符對任何值都適用,不過,當應用于不同的值時,遞減和遞增操作符會遵循下列規則:
- 當操作數是一個包含有效數字字符的字符串,系統會將其轉換為數字值,再執行遞減或遞增。
- 當操作數是一個不包含有效數字字符的字符串,系統將變量的值設置為NaN
- 當操作數是布爾值,會將其轉為數值(true轉為1,false轉為0)再操作。
- 當操作數是浮點數值,直接執行遞減或遞增
- 當操作數是對象,先調用對象的valueOf()方法取得一個可供操作的值,然后再遵循上面的三條規則。如果結果是NaN,則在調用toString()方法后再遵循上面的規則轉換。
```
var a = '2';
var b = 'a';
var c = false;
var d = 1.1;
var o = {
valueOf: function() {
return -1;
}
};
a++; // 3
b++; // NaN
c--; // -1
d--; // 0.10000000000000009 (浮點數操作結果,類似0.1+0.2 != 0.3)
o--; -2
```
**2.一元加和減運算符**
一元加(+)和減(-)運算符和數學上的含義一樣,表示負正。
不過,對非數值應用一元加運算符時,該運算符就像Number()轉型函數一樣對這個值執行轉換。(轉換規則參考數據類型那一節中的Number()的轉換規則)
一元減運算符對數值是表示負數,而對于非數值,轉換規則和一元加(也就是和Number())一樣,然后將轉換后的數值轉為負數。
**1.2 位運算符**
位運算符是按內存中表示數值的位來操作數值。
ECMAScript中的數值都以IEEE-75464位格式存儲,但位運算符并不直接操作64位的值,而是先將64位的值轉換為32位,然后執行操作,最后將結果轉換回64位。
對于有符號的整數,32位(從右往左,最右邊為第一位)中的前31位用于表示整數的值,第32位用于表示數值的符號:0表示正數,1表示負數。這個表示符號的位叫做符號位。符號位的值決定了其他位數值的格式。
正數以純二進制格式存儲,31位中的每一位(從右往左)都表示2的冪。第一位(叫做位0)表示2^0,第二位表示2^1,以此類推。沒有用到的位以0填充,可以忽略。
```
18
二進制表示法:10010
2^4 × 1 + 2^3 × 0 + 2^2 × 0 + 2^1 × 1 + 2^0 × 0 = 18
```
負數同樣以二進制碼存儲,但使用的格式是二進制補碼。
如何計算一個數值的二進制補碼?需要3個步驟:
* 求這個數值絕對值的二進制碼
* 求二進制反碼,即0替換1,將1替換0
* 得到二進制反碼加1
```
-18
//第一步,求18的二進制碼
0000 0000 0000 0000 0000 0000 0001 0010
// 求反碼,后加1
1111 1111 1111 1111 1111 1111 1110 1101
1
1111 1111 1111 1111 1111 1111 1110 1110 // -18的二進制表示
```
如果對非數值應用位運算符,會先使用Number()函數將該值轉換為一個數值(自動轉換),再進行位運算符操作,最后得到一個數值
當你去求負數的二進制時,ECMAScript會盡力向我們隱藏所有這些信息。比如:
```
var num = -18;
console.log(num.toString(2)); // "-10010"
```
上面的代碼對負數求二進制碼,得到的結果只是這個負數絕對值的二進制碼前面加上一個負號。
**(1)按位非(NOT)**
按位非運算符(~)進行`否運算`,執行按位非的結果就是返回數值的反碼。
```
~ 3 // -4
```
上面的表達式對3進行否運算,得到-4。
```
//3的二進制碼
00000000000000000000000000000011
// 取反碼
11111111111111111111111111111100
//補碼轉為十進制的規則,在下面可以看到
11111111111111111111111111111011 //減1
00000000000000000000000000000100 //再次取反得到~3的十進制,也就是4,加上負號,就是-4
```
負數二進制碼(補碼)轉換為十進制的規則:需要將這個數減去1,再取一次反,然后加上負號,才能得到這個負數對應的10進制值。
看起來是不是眼花繚亂,其實我們可以不用管里面的轉換邏輯,只需記得按位非(否運算)就是**一個數與自身的取反值相加,等于-1**。
```
~3 + 3 = -1 => -1 - 3 = -4 == ~3
```
**2.按位與(AND)**
使用按位與(&)運算符來進行`與運算`,它有兩個運算符數。
```
0 & 3 //0
0和1的二進制是00和11
00
11
00
```
運算規則是只有兩個數值的對應位都是1時才返回1,其他情況返回0。
**按位或(OR)**
使用按位或(|)運算符進行`或運算`.
```
0 | 3 // 3
00
11
11
```
或運算的規則是只要有一個位是1就返回1,只有兩個位都是0時才返回0
位運算只對整數有效,遇到小數時,會將小數部分舍去,只保留整數部分。所以,將一個小數與0進行或運算,等同于對該數去除小數部分,即取整數位。
**4.按位異或(XOR)**
使用按位異或(^)來進行`異或運算`。
```
0 ^ 3 // 3
00
11
11
```
異或運算的規則是兩個數值的對應位上只有一個1時才返回1,其他情況返回0.
**5.左移**
左移運算符(<<),這個運算符會將所有位向左移動指定的位數,尾部補0。
```
2 << 5 // 64
2的二進制碼
10
1000000
```
左移可看做是數值乘以2的指定次方
注意:左移不會影響操作數的符號位,也就是說,-2向左移動5位后,是-64
**6.右移**
**(1)有符號的右移**
有符號的右移(>>)將數值向右移動,但保留符號位(即正負號標記,也就是第32位)
```
64 >> 5 // 2
1000000
10
-64 >> 5 //-2
```
有符號的右移可看做是數值除以2的指定次方
**(2)無符號的右移**
無符號的右移(>>>),這個運算符會將數值的所有32位向右移動。對正數來說,無符號右移的結果與有符號右移相同:
```
64 >>> 5 //2
```
但負數就不一樣了,無符號右移是以0來填充空位的,所以無符號右移得到的結果都是正數。
```
-64 >>> 5 // 134217726
```
有一點需要特別注意,位運算符只對整數起作用,如果一個運算子不是整數,會自動轉為整數后再執行。
**1.3 布爾運算符**
布爾運算符是用來比較兩個值的關系的。
**1. 邏輯非(or)**
邏輯非(!)可以應用于JavaScript中的任何值,最終都會返回一個布爾值。
邏輯非運算符首先會將它的操作數轉換為一個布爾值,然后再取反。
**邏輯非規則**:
* 如果操作數是一個對象、非空字符串、任意非0數值(包括Infinity),則返回false
* 如果操作數是空字符串、數值0、null、undefined、NaN,則返回true
如果同時使用兩個(!!),就會像使用Boolean()轉型函數一樣的轉換邏輯。
```
!false // true
!'tg' // false
!'' // true
!!0 //false
```
**2.邏輯與**
邏輯與(&&)有兩個操作數,如果是布爾值,只有兩個都是true時,才會返回true,否則返回false
```
var isTrue = true && false; //false
```
如果不是布爾值,它遵循下面的規則:
* 如果第一個操作數是對象,則返回第二個操作數
* 如果第二個操作數是對象,則只有在第一個操作數的求值為true時才會返回第二個操作數
* 如果有一個操作數是null,則返回null
* 如果有一個操作數是NaN,則返回NaN
* 如果有一個操作數是undefined,則返回undefined
邏輯與操作符也就是先將第一個操作數轉換為Boolean類型判斷是true或false,再根據結果決定是否執行第二個操作數
```
0 && 'tg' ; // 0
{} && 'tg'; // "tg"
```
邏輯與操作屬于短路操作,也就是說如果第一個操作數能夠決定結果(等于false時),就不會再對第二個操作數求值。
**3.邏輯或**
邏輯或(||)也有兩個操作數。
如果兩個操作數都是布爾值,則有一個為true時,就返回true,否則返回false。
如果有一個操作數不是布爾值,則遵循下列規則:
* 如果第一個操作數是對象,則返回第一個操作數
* 如果第一個操作數的求值結果是false,則返回第二個操作數
* 如果兩個操作數都是對象,則返回第一個操作數
* 如果兩個操作數都是null,則返回null
* 如果兩個操作數都是NaN,則返回NaN
* 如果兩個操作數都是undefined,則返回undefined
邏輯或也是短路運算符,也就是說,如果第一個操作數的求值結果為true,就不會對第二個操作數求值了。
```
true || 'tg'; // true
0 || 'tg'; // "tg"
```
一般情況下,我們可以使用邏輯或來避免變量賦null或undefined值:
```
function test(name){
name = name || 'tg';
console.log(name);
}
test(); // "tg"
test('tg2'); // "tg2"
```
上面的例子,表示當調用test()方法不傳參時,name賦予一個默認值"tg";如果帶有參數,則使用參數值。
注意:邏輯與(&&)和邏輯或(||)返回的都是運算值。
**1.4 乘性運算符**
ECMAScript定義了3個乘性運算符:乘法、除法和求模。
當操作數是非數值時,會執行自動的類型轉換。如果操作數不是數值,會先使用Number()轉型函數將其轉換為數值(后臺自動),再進行運算。
**1.乘法**
乘法運算符(`*`),用于計算兩個數值的乘積。
處理特殊值時,乘法運算符會遵循下列規則:
* 如果操作數都是數值,但乘積超過了ECMAScript數值范圍,則返回Infinity或-Infinity
* 如果有一個操作數是NaN,結果是NaN
* 如果是Infinity乘以0,結果是NaN
* 如果是Infinity與非0數值相乘,結果是Infinity或-Infinity,取決于非0數值的符號
* 如果是Infinity與Infinity相乘,結果是Infinity
* 如果有一個操作數不是數值,則在后臺調用Number()將其轉換為數值,然后遵循上面的規則
```
console.log(1 * NaN); // NaN
console.log( Infinity * 2); // Infinity
console.log(Infinity * 0); // NaN
console.log(Infinity * Infinity); // Infinity
```
**2.除法**
除法運算符(/),執行第二個操作數除第一個操作數計算。
處理特殊值,規則如下:
* 如果操作數都是數值,但商超過了ECMAScript的表示范圍,則返回Infinity或-Infinity
* 如果有一個操作數是NaN,結果是NaN
* 如果是Infinity被Infinity除,結果是NaN
* 如果是零被零除,結果是NaN
* 如果是非零的有限數被零除,結果是Infinity或-Infinity,取決于有符號的操作數
* 如果是Infinity被任何非零數值除,結果是Infinity或-Infinity
* 如果有一個操作數不是數值,則在后臺調用Number()將其轉換為數值,然后遵循上面的規則。
```
console.log(NaN / 1); // NaN
console.log(0 / 0); // NaN
console.log(1 / 0); // Infinity
console.log(2 / Infinity); // 0
console.log(Infinity / Infinity); // NaN
console.log(Infinity / 2); // Infinity
```
**3.求模**
求模(余數)運算符(%)
處理特殊值,規則如下:
* 如果被除數是無窮大值而除數是有限大的數值,結果是NaN
* 如果被除數是有限大的數值而除數是零,結果是NaN
* 如果是Infinity被Infinity除,結果是NaN
* 如果被除數是有限大的數值而除數是無窮大的數值,結果是被除數
* 如果被除數是零,結果是零
* 如果有一個操作數不是數值,則在后臺調用Number()將其轉換為數值,然后遵循上面的規則。
```
console.log(5 % 3); // 2
```
**1.5加性運算符**
加性運算符也會在后臺轉換不同的數據類型。
**1.加法**
如果兩個操作數都是數值,執行常規的加法計算,然后根據下面的規則返回結果:
* 如果有一個操作數是NaN,結果是NaN
* 如果Infinity加Infinity,結果是Infinity
* 如果是-Infinity加-Infinity,結果是-Infinity
* 如果是Infinity加-Infinity,結果是NaN
* 如果是+0加+0,結果是+0
* 如果是-0加-0,結果是-0
* 如果是+0加-0,結果是+0
如果有一個操作數是字符串,則遵循下列規則:
* 如果兩個操作數都是字符串,則拼接兩個字符串
* 如果只有一個操作數是字符串,則將另一操作數轉換為字符串,然后拼接
* 如果有一個操作數是對象、數值或布爾值,則調用它們的toString()方法取得相應的字符串值,然后遵循上面的規則。對于undefined和null,則分別調用String()函數并取得字符串"undefined"和"null"。
```
5 + 5 //10
5 + '5" // "55"
```
如果你要在有字符串操作數的加法中執行常規的加法操作,應該使用圓括號將要相加的數值括起來:
```
'tg' + 5 + 5 // "tg55"
'tg" + (5 + 5) // "tg10"
```
**2.減法**
減法運算符(-)
對于特殊值,減法操作會遵循下列規則:
* 如果有一個操作數是NaN,結果是NaN
* 如果Infinity減Infinity,結果是NaN
* 如果是-Infinity減-Infinity,結果是NaN
* 如果是Infinity減-Infinity,結果是Infinity
* 如果是-Infinity減Infinity,結果是-Infinity
* 如果是+0減+0,結果是+0
* 如果是-0加-0,結果是+0
* 如果是+0減-0,結果是-0
* 如果有一個操作數是字符串、布爾值、null或undefined,則先在后臺調用Number()將其轉換為數值,然后遵循上面的規則進行計算。
* 如果有一個操作數是對象,則調用對象的valueOf()方法以取得表示該對象的數值;如果該對象沒有valueOf()方法,則調用其toString()方法將得到的字符串轉換為數值,然后遵循上面的規則進行計算。
```
5 - true; // 4 (true轉換成1)
5 - '2' // 3
5 - null; // 5(null轉換成0)
```
**1.6 關系運算符**
小于(<)、大于(>)、小于等于(<=)和大于等于(>=)四個關系運算符用來對兩個值進行比較,最后返回一個布爾值。
當使用了非數值時,則會遵循下列規則:
* 如果兩個操作數是字符串,則比較兩個字符串對應的字符編碼值
* 如果一個操作數是數值,則將另一個操作數轉換為一個數值,然后進行比較
* 如果一個操作數是對象,則調用這個對象的valueOf()方法,用得到的結果按照前面的規則比較。如果對象沒有valueOf()方法,則調用toString()方法,并用得到的結果按照上面的規則進行比較。
* 如果一個操作數是布爾值,則先將其轉換為數值,然后進行比較。
**注意**:在使用關系運算符比較兩個字符串時,比較的是兩個字符串中對應位置的每個字符的字符編碼值(從左往右),經過逐個比較后,直到有勝負(也就是不相等時),再返還布爾值。
```
'abc' > 'abd' //false
'A' < 'a' // true
'23' < '3' // true
```
在上面的例子中,第一行代碼會先比較"a"和"a",然后是"b"和"b",最后是"c"和"d",由于c的字符編碼值是63,d的字符編碼值是64,所以返還false。
由于小寫字母的字符編碼值總是大于大寫字母的字符編碼值,所以第二行代碼返還true。
第三行代碼也是字符串比較,所以比較的是字符編碼值(2是50,3是51),所以返回true
注意:任何操作數與NaN比較,都會返回false。
**1.7 相等運算符**
相等運算符有兩組:相等和不相等(先轉換再比較)、全等和不全等(只比較不轉換)
**1.相等和不相等**
相等(==),不相等(!=)
這兩個運算符都會先轉換操作數(強制轉換),然后比較。
對于不同的數據類型,轉換規則如下:
* 如果有一個操作數是布爾值,則在比較前先將其轉換為數值(false轉為0,true轉為1)
* 如果一個操作數是字符串,另一個操作數是數值,先將字符串轉為數值
* 如果一個操作數是對象,另一個不是,先調用對象的valueOf()方法,用得到的基本類型值按照前面的規則進行比較
* null和undefined是相等的
要比較之前,不能將null和undefined轉換成其他任何值
* 如果有一個操作數是NaN,則相等運算符返回false,不相等運算符返回true。(要記住NaN不等于本身原則)
* 如果兩個操作數都是對象,則比較它們是不是同一個對象。如果兩個操作數都指向同一個對象,則相等運算符返回true,否則返回false。
```
NaN == NaN // false
true == 1 // true
null == undefined // true
```
**2.全等和不全等**
除了在比較之前不轉換操作數類型之外,全等和不全等與相等和不相等沒什么區別。
```
'55' == 55 // true
'55' === 55 // false
```
**1.8 條件運算符**
條件運算符是ECMAScript中唯一的三元運算符。
```
variable = boolean_expression ? true_value : false_value;
```
如果boolean_expression返回true,則執行true_value,否則,執行false_value
```
var name = (1 > 2) ? 'tg' : 'tg2'; // "tg2"
```
上面的代碼中1小于2,所以是false,則將"tg2"賦值給name。
**1.9 賦值運算符**
(=)是最簡單的賦值操作,其作用是把右側的值賦給左側變量。
```
var name = 'tg';
```
復合賦值運算符
```
乘賦值(x *= y) 等同于 x = x * y
除賦值(x /= y) 等同于 x = x / y
模賦值(x %= y) 等同于 x = x % y
加賦值(x += y) 等同于 x = x + y
減賦值(x -= y) 等同于 x = x - y
左移賦值(x <<= y) 等同于 x = x << y
有符號右移賦值(x >>= y) 等同于 x = x >> y
無符號右移賦值(x >>>= y) 等同于 x = x >>> y
```
例子:
```
var a = 1;
a += 1;
console.log(a); // 2
```
**1.10 逗號運算符**
使用逗號運算符可以在一條語句中執行多個操作:
```
var name = 'tg', age = 1;
```
逗號運算符多用于聲明多個變量。
逗號運算符還可以用于賦值。在用于賦值時,逗號運算符總會返回表達式中的最后一項:
```
var num = (1,5,3); // num的值為3
```
3是表達式中的最后一項,因此num的值就是3.
**1.11 in運算符**
in運算符希望它的左操作數是一個字符串或可以轉換成為字符串,希望它的右操作數是一個對象。如果右側的對象擁有一個名為左操作數值的屬性名,則返回true。
```
var o = {x:1};
"x" in o //true
```
**1.12 instanceof運算符**
`instanceof`運算符希望左操作數是一個對象,右操作數標識對象的類。如果左側的對象是右側類的實例,則表達式返回true。
```
var a = new Array();
a instanceof Object; //true
```
注意:所有對象都是Object的實例
**1.13 typeof運算符**
`typeof`是一元運算符,用來判斷數據類型:
```
typeof 1 // "number"
```
**1.14 delete運算符**
`delete`是一元運算符,它用來刪除對象的屬性或數組元素。
```
var o={x:1}
delete o.x;
"x" in o //false
```
**1.15 void運算符**
`void`是一元運算符,它出現在操作數之前,操作數可以是任意類型。操作數會照常計算,但忽略計算結果并返回undefined。
```
void 0 //undefined
void(0) //undefined
var a = 1;
void (a=2);
a //2
```
這個運算符主要是用于書簽工具(bookmarklet),以及用于在超級鏈接中插入代碼,目的是返回undefined可以防止網頁跳轉。
```
<a href="javascript:void(0)"></a>
```
**2. 運算順序**
**2.1 優先級**
JavaScript各種運算符的優先級別(Operator Precedence)是不一樣的。優先級高的運算符先執行,優先級低的運算符后執行。
**2.2 圓括號**
圓括號(())可以用來提高運算的優先級,因為它的優先級是最高的,即圓括號中的表達式會第一個運算。
注意:因為圓括號不是運算符,所以不具有求值作用,只改變運算的優先級。
對于優先級別相同的運算符,大多數情況,計算順序總是從左到右,這叫做運算符的“左結合”(left-to-right associativity),即從左邊開始計算。
但是少數運算符的計算順序是從右到左,即從右邊開始計算,這叫做運算符的“右結合”(right-to-left associativity)。其中,最主要的是賦值運算符(=)和三元條件運算符(?:)。
- 前言
- JavaScript簡介
- 基本概念
- 語法
- 數據類型
- 運算符
- 表達式
- 語句
- 對象
- 數組
- 函數
- 引用類型(對象)
- Object對象
- Array對象
- Date對象
- RegExp對象
- 基本包裝類型(Boolean、Number、String)
- 單體內置對象(Global、Math)
- console對象
- DOM
- DOM-屬性和CSS
- BOM
- Event 事件
- 正則表達式
- JSON
- AJAX
- 表單和富文本編輯器
- 表單
- 富文本編輯器
- canvas
- 離線應用
- 客戶端存儲(Cookie、Storage、IndexedDB)
- HTML5 API
- Video/Audio
- Geolocation API
- requestAnimationFrame
- File API
- FullScreen API
- IndexedDB
- 檢測設備方向
- Blob
- vibrate
- Luminosity API
- WebRTC
- Page Visibility API
- Performance API
- Web Speech
- Notification
- 面向對象的程序設計
- 概述
- this關鍵字
- 原型鏈
- 作用域
- 常用API合集
- SVG
- 錯誤處理機制
- JavaScript開發技巧合集
- 編程風格
- 垃圾回收機制