也許你們會以爲js強制類型轉換與操做符知道個大概就夠了。是的,對於搬磚工來講,夠了。可是做爲一個有追求的前端,我深信只有掌握了別人不肯意掌握的東西,才能讓本身變得更強大更有競爭力。
或許你們不喜歡隱式類型轉換,以爲這東西太沒人性。可是你有沒有想過,這也許正是js語言的獨特之處?我認同kyle大佬說的,若是你完全掌握了隱式類型轉換,那麼對你來講,它就是「顯式」類型轉換了。
之因此先講類型轉換,是由於在操做符運算中涉及了大量的隱式類型轉換。javascript
抽象操做ToPrimitive用於將引用類型轉爲原始類型。實現細節比較複雜,有興趣的童鞋能夠參考這裏。前端
//模擬一個對象的轉基本類型操做 ToPrimitive var o = {}; o[Symbol.toPrimitive] = function(hint) { console.log(hint) //hint字符串至爲 string number default 中的一個 if (hint == "default" || hint == "number") { if (o.valueOf && typeof(o.valueof()) != 'object') { return o.valueOf() } else { return o.toString() } } else { if (o.toString && typeof(o.toString()) != 'object') { return o.toString() } else { return o.valueOf() } } } String(o) // string Number(o) // number 1+o // default 1-o // number o++ // number ++o // number
規則以下:java
string
(目前只有調用String()函數是執行這個順序):首先檢查該值是否有toString()方法。若是有而且返回基本類型值,就使用該值進行強制類型轉換。若是沒有就檢查該值是否有valueOf()方法。若是有而且返回基本類型值就使用該回值來進行強制類型轉換,若是沒有或者返回的不是基本類型值,就拋出錯誤。number/default
(常見強制類型轉換都是這個順序):首先檢查該值是否有valueOf()方法。若是有而且返回基本類型值,就使用該值進行強制類型轉換。若是沒有就檢查該值是否有toString()方法。若是有而且返回基本類型值就使用該回值來進行強制類型轉換,若是沒有或者返回的不是基本類型值,就拋出錯誤。抽象操做 ToString,負責處理非字符串到字符串的強制類型轉換。當須要一個值的字符串形式,就會進行 ToString 類型轉換。segmentfault
String()函數就會執行抽象操做 ToString,遵循下列轉換規則:數組
String() // '' String(0) // '0' String(true) // 'true' String(null) // 'null' String(undefined) // 'undefined' String(Symbol('asdf')) // "Symbol('asdf')" String({}) // '[Object object]' // 數組的默認 toString() 方法通過了從新定義,將全部單元字符串化之後再用 "," 鏈接起來 String([]) // '' String([1,2,3,4,'asdf']) // '1,2,3,4,asdf'
抽象操做 ToNumber,負責處理非數字到數字的強制類型轉換。函數
Number()執行抽象操做 ToNumber,函數的轉換規則以下。學習
Number() // 0 Number('') // 0 Number(' ') // 0 Number('0') // 0 Number('asdf') // NaN Number(true) // 1 Number(false) // 0 Number(null) // 0 Number(undefined) // NaN 與null不一樣,須要注意 // 對象會先經過抽象操做ToPrimitive轉爲基本類型,而後再轉數字 Number({}) // NaN Number([]) // 0 Number(['']) // 0 Number([' ']) // 0 Number(['0']) // 0 Number([1,2]) // NaN
抽象操做 ToBoolean,負責處理非布爾值到布爾值的強制類型轉換。編碼
轉換爲 boolean 類型是最爲簡單的一個。轉換規則以下:code
(1) 能夠被強制類型轉換爲 false
的值對象
(2) 其餘值會被被強制類型轉換爲 true
這裏有一個概念須要先理解:js的操做符和操做數組成了表達式,表達式一定會返回一個值。不管是一元操做++a
,仍是布爾操做[] || false
,都會返回一個值。另外關於js運算符優先級請參閱MDN的: 運算符優先級。
// 假設存在變量a +a // 一元加操做符 -a // 一元減操做符 ++a // 前置遞增操做符 --a // 前置遞減操做符 a++ // 後置遞增操做符 a-- // 後置遞減操做符
一元操做符指的是隻能操做一個值的操做符,區別與加性操做符能夠操做兩個值(如a+b
)。
一元加操做符+
用於非數字的強制類型轉換,做用等同於Number()
。如:
+'1.1' // 1.1 +'asdf' // NaN +true // 1 +false // 0 +null // 0 +undefined // NaN +{} // NaN +[] // 0 +new Date() // 1556258367546
一元減操做符-
行爲與+
相似,只不過最終運算結果是負數。如-true
結果是-1
。
不一樣於一元加減操做符,遞增遞減操做符只能做用於number
類型。若用於其餘類型會直接拋錯。
//前置遞增 var a = 57; var b = ++a; console.log(b); // 58 //後置遞增 var a = 57; var b = a++; console.log(b); // 57
前置遞增和後置遞增的區別在於,前置遞增++a
的返回值是增長1
的,然後置遞增a++
的返回值是不增長的。
遞減和遞增規則同樣,再也不廢話。
+
+
操做符經常使用於數學的計算和字符串的拼接,規則以下:
1+1 // 2 NaN+1 // NaN 'asdf'+'ghjk' // 'asdfghjk' 1+1+'1' // '21' []+1 // 1 null+undefined // 'nullundefined' []+{}+1 // '[Object object]1' 實際執行:''+'[Object object]'+1 {}+[]+1 // 1 {}位於行首會被解釋爲代碼塊,此處代碼塊被忽略,所以實際執行:+[]+1,結果爲數字1
-
1-1 // 0 NaN-1 // NaN 10-true-null // 9 10-true-undefined // NaN []-1 // 0 ['11']-11 // 0 11-{} // NaN
乘性操做符包括乘法*
、除法/
、除餘(求模)%
。規則以下:
數值計算較爲特殊的以下:
Infinity*0 // NaN Infinity/Infinity // NaN 0/0 // NaN Infinity%a // NaN a爲任意數值 a%0 // NaN a爲任意數值
!
邏輯非操做符會將任意值轉換爲一個布爾值,轉換規則和Boolean()
函數相反。連續使用兩個邏輯非操做符,等同於調用了Boolean()
。常見有大牛寫代碼用!!isTrue
來代替Boolean(isTrue)
函數。
!undefined // true !!undefined // false !NaN // true !!NaN // false !1234 // false !!1234 // true !'' // true !!'' // false
||
短路操做:若是第一個操做數可以決定結果,那麼就不會再對第二個操做數求值。
邏輯或操做符是短路操做,若是第一個操做數的求值結果(布爾求值,下同)爲true
,則直接返回第一個操做數,再也不對第二個操做數求值。若是第一個操做符求職結果爲false
,則返回第二個操做數。所以,常見大神寫代碼isExist || getIsExist()
,就是利用的短路操做,若是isExist
求值結果爲true,就再也不執行getExist()
函數。
[] || 0 // [] 對象(包括數組、函數等)的求值結果永遠爲`true`,直接返回這個對象 0 || [] // [] 1 || [] // 1 NaN || 0 // 0
&&
邏輯與操做屬於短路操做,即若是第一個操做數求值結果爲false
,則直接返回第一個操做數,那麼就不會再對第二個操做數求值。若是第一個操做數求值爲true
,則返回第二個操做數。能夠用來作條件限制obj && obj.value
。只有obj
對象存在了,纔會取obj.value
值。
0 && true // 0 null && [] // null NaN && null // NaN [] && {} // {}
須要注意布爾操做符存在優先級:! > && > ||
:
null || !2 || 3 && 4 // ??????你知道結果嗎?實際上,代碼至關於下面一行 null || (!2) || (3 && 4) // 4
相等操做符有== != === !==
四個,其中相等和不相等實行先轉換類型再比較,全等和不全等實行僅比較而不轉換類型。相等操做符返回布爾值true
或false
。
不一樣類型操做數比較規則以下:
[] == ![] // true /* 首先,布爾操做符!優先級更高,因此被轉變爲:[] == false * 其次,操做數存在布爾值false,將布爾值轉爲數字:[] == 0 * 再次,操做數[]是對象,轉爲原始類型(先調用valueOf(),獲得的仍是[],再調用toString(),獲得空字符串''):'' == 0 * 最後,字符串和數字比較,轉爲數字:0 == 0 */ NaN == NaN // false NaN不等於任何值 null == undefined // true null == 0 // false undefined == 0 // false
全等和不全等在比較以前不轉換類型,因此相對簡單:
null === undefined // false '1' === 1 // false 0 === false // false [] === [] // false 引用類型比較相等性還要看是否指向同一個內存地址 NaN === NaN // false NaN比較特殊,不等於自身
關係操做符小於(<)、大於(>)、小於等於(<=)和大於等於(>=)比較兩個值的大小,返回一個布爾值。當有一個操做數是非數值時,就會發生類型轉換:
'23' <'3' // true 比較的是字符編碼值 '23' < 3 // false 執行規則3 NaN > 0 // false NaN比較總會返回false null >= 0 // true 執行規則3,注意null相等性比較和關係比較不同 undefined >= 0 //false undefined執行關係比較會轉化爲NaN,老是返回false
三元表達式就是由條件操做符? :
組成:
a > b ? a : b; // 若是 ? 前的操做求值爲 true ,則返回 a ,不然返回 b
js中等號 =
用於賦值操做,var a = 1
就是把值1
賦值給變量a
。能夠和+ - * / %
構成複合賦值:
a += b // 等同於 a = a + b a -= b // 等同於 a = a - b a *= b // 等同於 a = a * b a /= b // 等同於 a = a / b a %= b // 等同於 a = a % b
逗號操做符經常使用於一條語句聲明多個變量:var a = 1, b = 2, c;
js中數值是以64位格式儲存的,前32位是整數,後32位是小數。位操做符會將64位的值轉爲32位的,因此位操做符會強制將浮點數轉爲整數。下面說幾個經常使用的位操做符:
~
:~x
至關於-(x+1)
,能夠用來代替indexOf
做爲判斷條件。~str.indexOf('asdf')
至關於str.indexOf('asdf')>-1
;|
:可用於將值截除爲一個 32 位整數。1.11 | 0
執行結果是1
js的類型轉換雖然很讓人頭疼,但並非無跡可尋。只要掌握了規則,就可以按規則判斷出來類型到底會如何轉換。而規則中很重要的一部分是,引用類型到基本類型的轉換是經過ToPrimitive
抽象操做完成的。掌握了ToPrimitive
抽象操做,就掌握了類型轉換的核心規則。
類型轉換是很常見和很經常使用的,雖然規則多了點,但卻值得去努力學習。