
1. 對象的相加
如果運算子是對象,必須先轉(zhuǎn)成原始類型的值,然后再相加。
var obj = { p: 1 };
obj + 2 // "[object Object]2"
// obj的轉(zhuǎn)換過程如下
obj.valueOf().toString() // "[object Object]"
2. 比較運算符
分成兩類:相等比較和非相等比較。兩者的規(guī)則是不一樣的,對于非相等的比較,算法是先看兩個運算子是否都是字符串,如果是的,就按照字典順序比較(實際上是比較 Unicode 碼點);否則,將兩個運算子都轉(zhuǎn)成數(shù)值,再比較數(shù)值的大小。
'2' > '11111' // true
5 > '4' // true
[2] > [11] // true
{ x: 2 } >= { x: 1 } // true
// 等同于 { x: 2 }.valueOf().toString() >= { x: 1 }.valueOf().toString()
// 即 '[object Object]' >= '[object Object]'
3. 相等運算符:== 和 ===
簡單說,它們的區(qū)別是相等運算符 == 比較兩個值是否相等,嚴格相等運算符 === 比較它們是否為“同一個值”。如果兩個值不是同一類型,嚴格相等運算符 === 直接返回false,而相等運算符 == 會將它們轉(zhuǎn)換成同一個類型,再用嚴格相等運算符進行比較。
復(fù)合類型值
兩個復(fù)合類型(對象、數(shù)組、函數(shù))的數(shù)據(jù)比較時,不是比較它們的值是否相等,而是比較它們是否指向同一個地址。
{} === {} // false
[] === [] // false
(function () {} === function () {}) // false
注意,對于兩個對象的比較,嚴格相等運算符比較的是地址,而大于或小于運算符比較的是值。
var obj1 = {};
var obj2 = {};
obj1 > obj2 // false
obj1 < obj2 // false
obj1 === obj2 // false
上面的三個比較,前兩個比較的是值,最后一個比較的是地址,所以都返回false。
== 涉及的隱式轉(zhuǎn)換
(1)原始類型值
原始類型的值會轉(zhuǎn)換成數(shù)值再進行比較。
'true' == true // false
// 等同于 Number('true') === Number(true)
// 等同于 NaN === 1
'\n 123 \t' == 123 // true
// 因為字符串轉(zhuǎn)為數(shù)字時,省略前置和后置的空格
(2)對象與原始類型值比較
對象(這里指廣義的對象,包括數(shù)組和函數(shù))與原始類型的值比較時,對象轉(zhuǎn)換成原始類型的值,再進行比較。
// 對象與數(shù)值比較時,對象轉(zhuǎn)為數(shù)值
[1] == 1 // true
// 等同于 Number([1]) == 1
// 對象與字符串比較時,對象轉(zhuǎn)為字符串
[1] == '1' // true
// 等同于 String([1]) == '1'
[1, 2] == '1,2' // true
// 等同于 String([1, 2]) == '1,2'
// 對象與布爾值比較時,兩邊都轉(zhuǎn)為數(shù)值
[1] == true // true
// 等同于 Number([1]) == Number(true)
[2] == true // false
// 等同于 Number([2]) == Number(true)
(3)undefined 和 null
undefined和null與其他類型的值比較時,結(jié)果都為false,它們互相比較時結(jié)果為true。
false == null // false
false == undefined // false
0 == null // false
0 == undefined // false
undefined == null // true
4. 余數(shù)運算符
運算結(jié)果的正負號由第一個運算子的正負號決定。
-1 % 2 // -1
1 % -2 // 1
所以,為了得到負數(shù)的正確余數(shù)值,可以先使用絕對值函數(shù)。
5. && 和 ||
&& 和 || 運算符的返回值并不一定是布爾類型,而是兩個操作數(shù)的其中之一。
他們首先會對第一個操作數(shù)執(zhí)行條件判斷,如果其不是布爾值,就先進行ToBoolean強制類型轉(zhuǎn)換,然后再執(zhí)行條件判斷。
&& 的運算規(guī)則是:如果第一個運算子的布爾值為true,則返回第二個運算子的值(注意是值,不是布爾值);如果第一個運算子的布爾值為false,則直接返回第一個運算子的值,且不再對第二個運算子求值。
這種跳過第二個運算子的機制,被稱為“短路”。有些程序員喜歡用它取代if結(jié)構(gòu)。
if (i) {
doSomething();
}
// 等價于
i && doSomething();
|| 的運算規(guī)則是:如果第一個運算子的布爾值為true,則返回第一個運算子的值,且不再對第二個運算子求值;如果第一個運算子的布爾值為false,則返回第二個運算子的值。
或運算符常用于為一個變量設(shè)置默認值。
function saveText(text) {
text = text || '';
// ...
}
// 或者寫成
saveText(this.text || '')
6. ? :
通常來說,三元條件表達式與if...else語句具有同樣表達效果,前者可以表達的,后者也能表達。但是兩者具有一個重大差別,if...else是語句,沒有返回值;三元條件表達式是表達式,具有返回值。所以,在需要返回值的場合,只能使用三元條件表達式,而不能使用if..else。
另外,三元運算符是右結(jié)合的,多個三元運算符嵌套,先計算最右邊的那個運算符。
7. 位運算符
這些位運算符直接處理每一個比特位(bit),所以是非常底層的運算,好處是速度極快,缺點是很不直觀,許多場合不能使用它們,否則會使代碼難以理解和查錯。
有一點需要特別注意,位運算符只對整數(shù)起作用,如果一個運算子不是整數(shù),會自動轉(zhuǎn)為整數(shù)后再執(zhí)行。另外,雖然在 JavaScript 內(nèi)部,數(shù)值都是以64位浮點數(shù)的形式儲存,但是做位運算的時候,是以32位帶符號的整數(shù)進行運算的,并且返回值也是一個32位帶符號的整數(shù)。
最快的取整方法:i = i | 0 或者 ~~i 或者i << 0
最快的交換方法:a^=b; b^=a; a^=b
<< 的應(yīng)用
下面代碼使用左移運算符,將顏色的 RGB 值轉(zhuǎn)為 HEX 值。
var color = {r: 186, g: 218, b: 85};
// RGB to HEX
// (1 << 24)的作用為保證結(jié)果是6位數(shù)
var rgb2hex = function(r, g, b) {
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b)
.toString(16) // 先轉(zhuǎn)成十六進制,然后返回字符串
.substr(1); // 去除字符串的最高位,返回后面六個字符串
}
rgb2hex(color.r, color.g, color.b)
// "#bada55"
>>> 的應(yīng)用
查看一個負整數(shù)在計算機內(nèi)部的儲存形式,最快的方法就是使用這個運算符。
-1 >>> 0 // 4294967295
上面代碼表示,-1作為32位整數(shù)時,內(nèi)部的儲存形式使用無符號整數(shù)格式解讀,值為 4294967295(即,等于11111111111111111111111111111111)
8. void
void運算符的作用是執(zhí)行一個表達式,然后不返回任何值,或者說返回undefined。
這個運算符的主要用途是瀏覽器的書簽工具(Bookmarklet),以及在超級鏈接中插入代碼防止網(wǎng)頁跳轉(zhuǎn)。
一個實際的例子,用戶點擊鏈接提交表單,但是不產(chǎn)生頁面跳轉(zhuǎn)。
<a href="javascript: void(document.form.submit())">
提交
</a>
9. 逗號運算符
逗號運算符用于對兩個表達式求值,并返回后一個表達式的值。
逗號運算符的一個用途是,在返回一個值之前,進行一些輔助操作。
var value = (console.log('Hi!'), true); // Hi!
value // true
10. ( )的注意事項
圓括號不是運算符,所以不具有求值作用,只改變運算的優(yōu)先級。
圓括號之中,只能放置表達式,如果將語句放在圓括號之中,就會報錯。
(var a = 1) // SyntaxError: Unexpected token var