# 表達式和運算符
## [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#表達式)表達式
表達式是由數字、運算符、數字分組符號(如括號)、自由變量和約束變量等以能求得數值的有意義排列方法所得的組合。JavaScript 表達式主要有以下幾種形式:
* 原始表達式:常量、變量、保留字。
* 對象、數組初始化表達式:`var obj={a:1,b:2};`,`var arr=[1,2,3];`。
* 函數定義表達式:`var fn=function(){}`。
* 屬性訪問表達式:`Math.abs`。
* 調用表達式:`alert('hello');`。
* 對象創建表達式:`new object();`。
## [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#運算符)運算符
JavaScript 中的運算符用于算術表達式、比較表達式、邏輯表達式、賦值表達式等。需要注意的是,大多數運算符都是由標點符號表示的,比如?`+`?和?`=`。而另外一些運算符則是由關鍵字表示的,比如?`typeof`?和?`instanceof`,關鍵字運算符和標點符號都是正規的運算符。
下表列出了 JavaScript 中所有的運算符,并按照運算符的優先級排序的,前面的運算符優先級要高于后面的運算符優先級,被空行分隔開來的運算符具有不同的優先級。標題為 A 的列表示運算符的結合性(Associativity),L 表示從左至右、R 表示從右至左,標題為 N 的列表示操作數的個數(Number)。
| 運算符 | 操作 | A | N |
| --- | --- | --- | --- |
| `++` | 前/后增量 | R | 1 |
| `--` | 前/后增量 | R | 1 |
| `-` | 求反 | R | 1 |
| `+` | 轉換為數字 | R | 1 |
| `~` | 按位求反 | R | 1 |
| `!` | 邏輯非 | R | 1 |
| `delete` | 刪除屬性 | R | 1 |
| `typeof` | 檢測類型 | R | 1 |
| `void` | 返回`undefined` | R | 1 |
| | | | |
| `*`?`/`?`%` | 乘,除,求模 | L | 2 |
| `+`?`-` | 加,減 | L | 2 |
| `+` | 字符串連接 | L | 2 |
| `<<` | 左移位 | L | 2 |
| `>>` | 有符號右移 | L | 2 |
| `>>>` | 無符號右移 | L | 2 |
| `<`?`<=`?`>`?`>=` | 比較數字順序 | L | 2 |
| `<`?`<=`?`>`?`>=` | 比較字母順序 | L | 2 |
| `instanceof` | 測試對象類 | L | 2 |
| `in` | 測試屬性是否存在 | L | 2 |
| | | | |
| `==` | 判斷相等 | L | 2 |
| `!=` | 判斷不等 | L | 2 |
| `===` | 判斷恒等 | L | 2 |
| `!==` | 判斷恒不等 | L | 2 |
| `&` | 按位與 | L | 2 |
| | | | |
| `^` | 按位異或 | L | 2 |
| `┃` | 按位或 | L | 2 |
| | | | |
| `&&` | 邏輯與 | L | 2 |
| | | | |
| `┃┃` | 邏輯或 | L | 2 |
| `?:` | 條件運算符 | R | 3 |
| | | | |
| `=` | 賦值 | R | 2 |
| `*=`?`/=`?`%=`?
`+=`?`-=`?`&=`?
`<<=`?`>>=`?
`^=`?`┃=`?`>>>=` | 運算且賦值 | R | 2 |
| | | | |
| `,` | 忽略第一個操作數,
返回第二個操作數 | L | 2 |
> 因為?`|`?是制表符,會導致格式混亂,所以表格中的?`|`?均以?`┃`?代替。
### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#一元運算符)一元運算符
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#delete-運算符)`delete`?運算符
`delete`?運算符用來刪除對象屬性或者數組元素,如果刪除成功或所刪除的目標不存在,`delete`?將返回?`true`。然而,并不是所有的屬性都可刪除,一些內置核心和客戶端屬性是不能刪除的,通過?`var`?語句聲明的變量不能刪除,通過?`function`?語句定義的函數也是不能刪除的。例如:
~~~
var o = { x: 1, y: 2}; // 定義一個對象
console.log(delete o.x); // true,刪除一個屬性
console.log(delete o.x); // true,什么都沒做,x 在已上一步被刪除
console.log("x" in o); // false,這個屬性在對象中不再存在
console.log(delete o.toString); // true,什么也沒做,toString是繼承來的
console.log(delete 1); // true,無意義
var a = [1,2,3]; // 定義一個數組
console.log(delete a[2]); // true,刪除最后一個數組元素
console.log(2 in a); // false,元素2在數組中不再存在
console.log(a.length); // 3,數組長度并不會因 delete 而改變
console.log(a[2]); // undefined,元素2所在的位置被空了出來
console.log(delete a); // false,通過 var 語句聲明的變量不能刪除
function f(args){} // 定義一個函數
console.log(delete f); // false,通過 function 語句聲明的函數不能刪除
~~~
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#void-運算符)`void`?運算符
`void`?運算符可以應用于任何表類型的表達式,表達式會被執行,但計算結果會被忽略并返回?`undefined`。例如:
~~~
void 0;
void "you are useless?";
void false;
void [];
void /(useless)/ig;
void function(){ console.log("you are so useless?"); }
// always return undefined
~~~
> 擴展閱讀「談談 JavaScript 中的 void 運算符」
> [https://segmentfault.com/a/1190000000474941](https://segmentfault.com/a/1190000000474941)
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#typeof-運算符)`typeof`?運算符
> 請參見[「變量和數據類型」-「數據類型」-「`typeof`?運算符」](https://github.com/stone0090/javascript-lessons/tree/master/1.4-Variable%26Types#typeof-%E8%BF%90%E7%AE%97%E7%AC%A6)。
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#----運算符)`++`?`--`?運算符
`++`?`--`?遞增遞減運算符借鑒自 C 語言,它們分前置型和后置型,作用是改變一個變量的值。例如:
~~~
var a = 5;
console.log(a++); // 5
console.log(++a); // 7
console.log(a--); // 7
console.log(--a); // 5
~~~
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#---運算符)`+`?`-`?運算符
當?`+`?`-`?作為一元運算符時,應用于數值,表示數值的正負。應用于非數值,先按?`Number()`?轉型函數對這個值執行轉換,再表示該值的正負。
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#--運算符)`~`?`!`?運算符
> `~`?按位非運算符,請參見下面[「位運算符」](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#%E4%BD%8D%E8%BF%90%E7%AE%97%E7%AC%A6)。
> `!`?邏輯非運算符,請參見下面[「邏輯運算符」](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#%E9%80%BB%E8%BE%91%E8%BF%90%E7%AE%97%E7%AC%A6)。
### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#乘性運算符)乘性運算符
JavaScript 定義了3個乘性運算符:乘法、除法和求模。這些運算符與 C 語言的相應運算符用途類似,只不過在操作數為非數值的情況下會執行自動的類型轉換。如果參與乘法計算的某個操作數不是數值,后臺會先使用?`Number()`?轉型函數將其轉換為數值。也就是說,空字符串將被當作?`0`,布爾值?`true`?將被當作?`1`。
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#-乘法運算符)`*`?乘法運算符
用于計算兩個數值的乘積,在處理特殊值的情況下,乘法運算符遵循下列特殊的規則:
* 如果操作數都是數值,執行常規的乘法計算,即兩個正數或兩個負數相乘的結果還是正數,而如果只有一個操作數有符號,那么結果就是負數。如果乘積超過了 JavaScript 數值的表示范圍,則返回?`Infinity`?或?`-Infinity`;
* 如果有一個操作數是?`NaN`,則結果是?`NaN`;
* 如果是?`Infinity`?與?`0`?相乘,則結果是?`NaN`;
* 如果是?`Infinity`?與非?`0`?數值相乘,則結果是?`Infinity`?或?`-Infinity`,取決于有符號操作數的符號;
* 如果是?`Infinity`?與?`Infinity`?相乘,則結果是?`Infinity`; 如果有一個操作數不是數值,則在后臺調用?`Number()`將其轉換為數值,然后再應用上面的規則。
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#-除法運算符)`/`?除法運算符
用于計算兩個數值的商,與乘法運算符類似,除法運算符對特殊的值也有特殊的處理規則。這些規則如下:
* 如果操作數都是數值,執行常規的除法計算,即兩個正數或兩個負數相除的結果還是正數,而如果只有一個操作數有符號,那么結果就是負數。如果商超過了 JavaScript 數值的表示范圍,則返回?`Infinity`?或?`-Infinity`;
* 如果有一個操作數是?`NaN`,則結果是?`NaN`;
* 如果是?`Infinity`?被?`Infinity`?除,則結果是?`NaN`;
* 如果是零被零除,則結果是?`NaN`;
* 如果是非零的有限數被零除,則結果是?`Infinity`?或?`-Infinity`,取決于有符號操作數的符號;
* 如果是?`Infinity`?被任何非零數值除,則結果是?`Infinity`?或?`-Infinity`,取決于有符號操作數的符號;
* 如果有一個操作數不是數值,則在后臺調用?`Number()`?將其轉換為數值,然后再應用上面的規則。
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#-求模運算符)`%`?求模運算符
用于計算兩個數值的余數,與另外兩個乘性運算符類似,求模運算符會遵循下列特殊規則來處理特殊的值:
* 如果操作數都是數值,執行常規的除法計算,返回除得的余數;
* 如果被除數是無窮大值而除數是有限大的數值,則結果是?`NaN`;
* 如果被除數是有限大的數值而除數是零,則結果是?`NaN`;
* 如果是?`Infinity`?被?`Infinity`?除,則結果是?`NaN`;
* 如果被除數是有限大的數值而除數是無窮大的數值,則結果是被除數;
* 如果被除數是零,則結果是零;
* 如果有一個操作數不是數值,則在后臺調用?`Number()`?將其轉換為數值,然后再應用上面的規則。
### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#加性運算符)加性運算符
加法和減法這兩個加性運算符應該說是編程語言中最簡單的算術運算符了。但是在 JavaScript 中,這兩個運算符卻都有一系列的特殊行為。與乘性運算符類似,加性運算符也會在后臺轉換不同的數據類型。然而,對于加性運算符而言,相應的轉換規則還稍微有點復雜。
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#-加法運算符)`+`?加法運算符
如果兩個運算符都是數值,執行常規的加法計算,然后根據下列規則返回結果:
* 如果有一個操作數是?`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"`。
* 如果是?`null`?加?`null`,則結果是?`0`;
* 如果是?`undefined`?加?`undefined`,則結果是?`NaN`;
下面來舉幾個例子:
~~~
var result1 = 5 + 5; // 兩個數值相加
console.log(result1); // 10
var result2 = 5 + "5"; // 一個數值和一個字符串相加
console.log(result2); // "55"
var num1 = 5;
var num2 = 10;
var message = "The sum of 5 and 10 is " + num1 + num2;
console.log(message); // "The sum of 5 and 10 is 510",如何修改?
~~~
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#--減法運算符)`-`?減法運算符
如果兩個運算符都是數值,執行常規的減法計算,然后根據下列規則返回結果:
* 如果有一個操作數是?`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()`?函數將其轉換為數值,然后再根據前面的規則執行減法計算。如果轉換的結果是?`NaN`,則減法的結果就是?`NaN`;
* 如果有一個操作數是對象,則調用對象的?`valueOf()`?方法以取得表示該對象的數值。如果得到的值是?`NaN`,則減法的結果就是?`NaN`。如果對象沒有?`valueOf()`?方法,則調用其?`toString()`方法并將得到的字符串轉換為數值。
* 如果是?`null`?減?`null`,則結果是?`0`;
* 如果是?`undefined`?減?`undefined`,則結果是?`NaN`;
下面來舉幾個例子:
~~~
var result1 = 5 - true; // 4,因為true被轉換成了1
var result2 = NaN - 1; // NaN
var result3 = 5 - 3; // 2
var result4 = 5 - ""; // 5,因為"" 被轉換成了0
var result5 = 5 - "2"; // 3,因為"2"被轉換成了2
var result6 = 5 - null; // 5,因為null被轉換成了0
~~~
### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#等值運算符)等值運算符
確定兩個變量是否相等是編程中的一個非常重要的操作。在比較簡單數據類型之間的相等性時,問題還比較簡單。但在涉及到對象之間的比較時,問題就變得復雜了。最早的 JavaScript 中的相等和不等運算符會在執行比較之前,先將對象轉換成相似的類型。后來,有人提出了這種轉換到底是否合理的質疑。最后,JavaScript 的解決方案就是提供兩組運算符:相等和不相等(先轉換再比較),恒等和不恒等(僅比較而不轉換)。
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#--運算符-1)`==`?`!=`?運算符
`==`?`!=`?這兩個運算符都會先轉換操作數(通常稱為強制轉型),然后再比較它們的相等性。在轉換不同的數據類型時,相等和不相等運算符遵循下列基本規則:
* 如果有一個操作數是布爾值,則在比較相等性之前先將其轉換為數值(`false`?轉換為?`0`,而?`true`?轉換為?`1`);
* 如果一個操作數是字符串,另一個操作數是數值,在比較相等性之前先將字符串轉換為數值;
* 如果一個操作數是對象,另一個操作數不是,則調用對象的?`valueOf()`?方法,用得到的基本類型值按照前面的規則進行比較;
* `null`?和?`undefined`?是相等的。 要比較相等性之前,不能將?`null`?和?`undefined`?轉換成其他任何值。
* 如果有一個操作數是?`NaN`,則相等運算符返回?`false`,而不相等運算符返回?`true`。重要提示:即使兩個操作數都是`NaN`,相等運算符也返回?`false`;因為按照規則,`NaN`?不等于?`NaN`。
* 如果兩個操作數都是對象,則比較它們是不是同一個對象。如果兩個操作數都指向同一個對象,則相等運算符返回`true`;否則,返回?`false`。
列出了一些特殊情況及比較結果:
~~~
null == undefined // true
"NaN" == NaN // false
5 == NaN // false
NaN == NaN // false
NaN != NaN // true
false == 0 // true
true == 1 // true
true == 2 // false
undefined == 0 // false
null == 0 // false
"5" == 5 // true
~~~
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#--運算符-2)`===`?`!==`?運算符
除了在比較之前不轉換操作數之外,恒等和不恒等運算符與相等和不相等運算符沒有什么區別。它只在兩個操作數未經轉換就相等的情況下返回?`true`,如下面的例子所示:
~~~
var result1 = ("55" == 55); // true,因為轉換后相等
var result2 = ("55" === 55); // false,因為不同的數據類型不相等
var result3 = (null == undefined) // true,因為它們是類似的值
var result4 = (null === undefined) // false,因為它們是不同類型的值
~~~
### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#關系運算符)關系運算符
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#----運算符-1)`<`?`>`?`<=`?`>=`?運算符
`<`?小于、`>`?大于、`<=`?小于等于、?`>=`?大于等于 這幾個關系運算符用于對兩個值進行比較返回一個布爾值。與 JavaScript 中的其他運算符一樣,當關系運算符的操作數使用了非數值時,也要進行數據轉換或完成某些奇怪的操作。以下就是相應的規則。
* 如果兩個操作數都是數值,則執行數值比較。
* 如果兩個操作數都是字符串,則比較兩個字符串對應的字符編碼值(可以通過字符串的?`charCodeAt()`?函數獲取字符編碼值)。
* 如果一個操作數是數值,則將另一個操作數轉換為一個數值,然后執行數值比較。
* 如果一個操作數是對象,則調用這個對象的?`valueOf()`?方法,用得到的結果按照前面的規則執行比較。如果對象沒有`valueOf()`方法,則調用?`toString()`方法,并用得到的結果根據前面的規則執行比較。
* 如果一個操作數是布爾值,則先將其轉換為數值,然后再執行比較。
請思考下面幾個例子的結果是如何得出的:
~~~
var result1 = "Brick" < "alphabet"; // true
var result2 = "brick" < "alphabet"; // false
var result3 = "23" < "3"; // true
var result4 = "23" < 3; // false
var result5 = "a" < 3; // false
var result6 = NaN < 3; // false
var result7 = NaN >= 3; // false
~~~
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#in-運算符)`in`?運算符
`in`?運算符希望它的左操作數是一個字符串或可以轉換為字符串,希望它的右操作數是一個對象。如果右側的對象擁有一個名為左操作數值的屬性名,那么表達式返回?`true`,例如:
~~~
var point = { x:1, y:1 }; // 定義一個對象
"x" in point // true,對象有一個名為"x"的屬性
"z" in point // false,對象中不存在名為"z"的屬性
"toString" in point // true,對象繼承了toString()方法
var data = [7,8,9]; // 擁有三個元素的數組
"0" in data // true,數組包含元素"0"
1 in data // true,數字轉換為字符串
3 in data // false,沒有索引為3的元素
~~~
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#instanceof-運算符)`instanceof`?運算符
`instanceof`?運算符希望左操作數是一個對象,右操作數標識對象的類。如果左側的對象是右側類的實例,則表達式返回`true`;否則返回?`false`。后面會講 JavaScript 中對象的類是通過初始化它們的構造函數來定義的。這樣的話,`instanceof`的右操作數應當是一個函數。比如:
~~~
var d = new Date(); // 通過 Date() 構造函數來創建一個新對象
d instanceof Date; // true,d 是由 Date() 創建的
d instanceof Object; // true,所有的對象都是 Object 的實例
d instanceof Number; // false,d 不是一個 Number 對象
var a = [1, 2, 3]; // 通過數組字面量的寫法創建一個數組
a instanceof Array; // true,a 是一個數組
a instanceof Object; // true,所有的數組都是對象
a instanceof RegExp; // false,數組不是正則表達式
~~~
需要注意的是,所有的對象都是?`Object`?的實例。當通過?`instanceof`?判斷一個對象是否是一個類的實例的時候,這個判斷也會包含對「父類」的檢測。如果?`instanceof`?的左操作數不是對象的話,`instanceof`?返回?`false`。如果右操作數不是函數,則拋出一個類型錯誤異常。
### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#邏輯運算符)邏輯運算符
邏輯運算符是對操作數進行布爾算術運算,經常和關系運算符一起配合使用,邏輯運算符將多個關系表達式組合起來組成一個更復雜的表達式。
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#-邏輯與)`&&`?邏輯與
邏輯與操作可以應用于任何類型的操作數,而不僅僅是布爾值。在有一個操作數不是布爾值的情況下,邏輯與操作不一定返回布爾值;此時,它遵循下列規則:
* 如果第一個操作數是對象,則返回第二個操作數;
* 如果第二個操作數是對象,則只有在第一個操作數的求值結果為?`true`?的情況下才會返回該對象;
* 如果兩個操作數都是對象,則返回第二個操作數;
* 如果有一個操作數是?`null`,則返回?`null`;
* 如果有一個操作數是?`NaN`,則返回?`NaN`;
* 如果有一個操作數是?`undefined`,則返回?`undefined`。
邏輯與操作屬于短路操作,即如果第一個操作數能夠決定結果,那么就不會再對第二個操作數求值。對于邏輯與操作而言,如果第一個操作數是?`false`,無論第二個操作數是什么值,結果都不再可能是?`true`?了。
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#-邏輯或)`||`?邏輯或
與邏輯與操作相似,如果有一個操作數不是布爾值,邏輯或也不一定返回布爾值;此時,它遵循下列規則:
* 如果第一個操作數是對象,則返回第一個操作數;
* 如果第一個操作數的求值結果為?`false`,則返回第二個操作數;
* 如果兩個操作數都是對象,則返回第一個操作數;
* 如果兩個操作數都是?`null`,則返回?`null`;
* 如果兩個操作數都是?`NaN`,則返回?`NaN`;
* 如果兩個操作數都是?`undefined`,則返回?`undefined`。
與邏輯與運算符相似,邏輯或運算符也是短路運算符。也就是說,如果第一個操作數的求值結果為?`true`,就不會對第二個操作數求值了。
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#--邏輯非)`!`?邏輯非
邏輯非操作可以應用于任何類型的操作數,無論這個值是什么數據類型,這個運算符都會返回一個布爾值。邏輯非運算符首先會將它的操作數轉換為一個布爾值,然后再對其求反。邏輯非運算符遵循下列規則:
* 如果操作數是一個對象,返回?`false`;
* 如果操作數是一個空字符串,返回?`true`;
* 如果操作數是一個非空字符串,返回?`false`;
* 如果操作數是數值?`0`,返回?`true`;
* 如果操作數是任意非?`0`?數值(包括?`Infinity`),返回?`false`;
* 如果操作數是?`null`,返回?`true`;
* 如果操作數是?`NaN`,返回?`true`;
* 如果操作數是?`undefined`,返回?`true`。
下面幾個例子展示了應用上述規則的結果:
~~~
console.log(!false); // true
console.log(!"blue"); // false
console.log(!0); // true
console.log(!NaN); // true
console.log(!""); // true
console.log(!12345); // false
~~~
邏輯非運算符也可以用于將一個值轉換為與其對應的布爾值。而同時使用兩個邏輯非運算符,實際上就會模擬?`Boolean()`?轉型函數的行為。其中,第一個邏輯非操作會基于無論什么操作數返回一個布爾值,而第二個邏輯非操作則對該布爾值求反,于是就得到了這個值真正對應的布爾值。當然,最終結果與對這個值使用?`Boolean()`?函數相同,例如:
~~~
console.log(!!"blue"); //true
console.log(!!0); //false
console.log(!!NaN); //false
console.log(!!""); //false
console.log(!!12345); //true
~~~
### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#位運算符)位運算符
在 JavaScript 中,當對數值應用位運算符時,后臺會發生如下轉換過程:64位的數值被轉換成32位數值,然后執行位操作,最后再將32位的結果轉換回64位數值。這個轉換過程導致了一個嚴重的副效應,即在對特殊的?`NaN`?和?`Infinity`?值應用位操作時,這兩個值都會被當成?`0`?來處理。如果對非數值應用位運算符,會先使用?`Number()`?函數將該值轉換為一個數值,然后再應用位操作,得到的結果將是一個數值。
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#-按位非)`~`?按位非
簡單的理解,對任一數值?`x`?進行按位非操作的結果為?`-(x+1)`。例如:
~~~
console.log(~null); // -1
console.log(~undefined); // -1
console.log(~0); // -1
console.log(~{}); // -1
console.log(~[]); // -1
console.log(~(1/0)); // -1
console.log(~false); // -1
console.log(~true); // -2
console.log(~1.2543); // -2
console.log(~4.9); // -5
console.log(~(-2.999)); // 1
~~~
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#-按位與)`&`?按位與
按位與操作就是將兩個數值的每一位對齊,兩個數值的對應位都是?`1`?時才返回?`1`,任何一位是?`0`,結果都是?`0`。如下表所示:
| 第一個數值的位 | 第二個數值的位 | 結果 |
| --- | --- | --- |
| 1 | 1 | 1 |
| 1 | 0 | 0 |
| 0 | 1 | 0 |
| 0 | 0 | 0 |
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#-按位或)`|`?按位或
按位或操作就是將兩個數值的每一位對齊,兩個數值只要有一個位是?`1`?就返回?`1`,只在兩個位都是?`0`?的情況下才返回?`0`。如下表所示:
| 第一個數值的位 | 第二個數值的位 | 結果 |
| --- | --- | --- |
| 1 | 1 | 1 |
| 1 | 0 | 1 |
| 0 | 1 | 1 |
| 0 | 0 | 0 |
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#-按位異或)`^`?按位異或
按位異或與按位或的不同之處在于,兩個數值只有一個?`1`?時才返回?`1`,如果對應的兩位都是?`1`?或都是?`0`,則返回?`0`。
| 第一個數值的位 | 第二個數值的位 | 結果 |
| --- | --- | --- |
| 1 | 1 | 0 |
| 1 | 0 | 1 |
| 0 | 1 | 1 |
| 0 | 0 | 0 |
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#-左移)`<<`?左移
這個運算符會將數值的所有位向左移動指定的位數。例如:
~~~
var oldValue = 2; // 等于二進制的 10
var newValue = oldValue << 5; // 等于二進制的 1000000,十進制的 64
~~~
注意,左移不會影響操作數的符號位。換句話說,如果將?`-2`?向左移動?`5`?位,結果將是?`-64`,而非?`64`。
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#-有符號的右移)`>>`?有符號的右移
這個運算符會將數值向右移動,但保留符號位(即正負號標記)。
~~~
var oldValue = 64; // 等于二進制的 1000000
var newValue = oldValue >> 5; // 等于二進制的 10 ,即十進制的 2
~~~
#### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#-無符號的右移)`>>>`?無符號的右移
這個運算符會將數值的所有32位都向右移動。對正數來說,無符號右移的結果與有符號右移相同。
~~~
var oldValue = 64; // 等于二進制的 1000000
var newValue = oldValue >>> 5; // 等于二進制的 10 ,即十進制的 2
~~~
無符號右移運算符會把負數的二進制碼當成正數的二進制碼。而且,由于負數以其絕對值的二進制補碼形式表示,因此就會導致無符號右移后的結果非常之大。
~~~
var oldValue = -64; // 等于二進制的 11111111111111111111111111000000
var newValue = oldValue >>> 5; // 等于十進制的 134217726
~~~
### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#賦值運算符)賦值運算符
簡單的賦值運算符由等于號?`=`?表示,其作用就是把右側的值賦給左側的變量,如下面的例子所示:
~~~
var num = 10;
~~~
如果在等于號?`=`?前面再添加乘性運算符、加性運算符或位運算符,就可以完成復合賦值操作。這種復合賦值操作相當于是對下面常規表達式的簡寫形式:
~~~
var num = 10;
num += 10; // 等同于 num = num + 10;
~~~
每個主要算術運算符(以及個別的其他運算符)都有對應的復合賦值運算符。這些運算符如下所示:
* 乘/賦值?`*=`;
* 除/賦值?`/=`;
* 模/賦值?`%=`;
* 加/賦值?`+=`;
* 減/賦值?`-=`;
* 左移/賦值?`<<=`;
* 有符號右移/賦值?`>>=`;
* 無符號右移/賦值?`>>>=`。
設計這些運算符的主要目的就是簡化賦值操作,使用它們不會帶來任何性能的提升。
### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#條件運算符)條件運算符
`? :`?條件運算符應該算是 JavaScript 中最靈活的一種運算符了,而且它遵循與 Java 中的條件運算符相同的語法形式,如下面的例子所示:
~~~
variable = boolean_expression ? true_value : false_value;
~~~
### [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#逗號運算符)逗號運算符
逗號運算符多用于聲明多個變量;但除此之外,逗號運算符還可以用于賦值。在用于賦值時,逗號運算符總會返回表達式中的最后一項,如下面的例子所示:
~~~
var num = (5, 1, 4, 8, 0); // num 的值為 0
~~~
由于?`0`?是表達式中的最后一項,因此?`num`?的值就是?`0`。雖然逗號的這種使用方式并不常見,但這個例子可以幫我們理解逗號的這種行為。
## [](https://github.com/stone0090/javascript-lessons/tree/master/1.5-Expression&Operators#關卡)關卡
~~~
// 挑戰一
var x=1;
if(!!function f(){}){
x+=typeof f;
}
console.log(x); // ???
~~~
~~~
// 挑戰二
(function f(f){
console.log(typeof f()); // ???
})(function(){return 1;});
~~~
~~~
// 挑戰三
console.log(typeof 2*3); // ???
console.log(typeof 2+3); // ???
~~~
~~~
// 挑戰四
var a=0,b=0;
console.log(a+++b); // ???
console.log(a); // ???
console.log(b); // ???
~~~
~~~
// 挑戰五
var a,b,c;
a=b==c;
console.log(a); // ???
~~~
~~~
// 挑戰六
console.log(1 && 3); // ???
console.log(1 && "foo" || 0); // ???
console.log(1 || "foo" && 0); // ???
~~~
~~~
// 挑戰七
var a=1;
var b=(a=(2,4,6))+a++
console.log(b); // ???
~~~
~~~
// 挑戰八
if (!("a" in window)) {
var a = 1;
}
console.log(a); // ???
~~~
~~~
// 挑戰九
var val = 'smtg';
console.log('Value is ' + (val === 'smtg') ? 'Something' : 'Nothing'); // ???
~~~
~~~
// 挑戰九
console.log(1 + - + + + - + 1);
~~~