開胃菜
先說一個題外話,我在工作中遇到一個問題,需要比較 "08:00"
和 "09:00"
的大小,最後我找到三種方法:
- 在兩個字符串前後各拼接相同的年月日和秒,拼成完整的時間格式進行比較:
var head = "2016-01-01 "
var foot = ":00"
var time1 = head + "08:00" + foot //"2016-01-01 08:00:00"
var time2 = head + "09:00" + foot //"2016-01-01 09:00:00"
剩下的就不說了,比較兩個完整的日期還是很容易的。
- 把兩個字符串中的冒號去掉,轉換成數字進行比較:
function timeToNumber(time) {
let [head,foot] = time.split(":")
return Number(head+foot)
}
var time1 = timeToNumber("08:00") //800
var time2 = timeToNumber("09:00") //900
- 直接比較
對,你沒有看錯,直接比較兩個字符串:
"08:00" > "09:00" //false
看到這裏估計有人就納悶了,很明顯第三種方法是更簡潔的,但是字符串比較,好像很少見,它比較的依據是什麼呢?
其實,字符串比較大小,會從左到右依次取兩個字符串中的字符,兩兩比較他們charCodeAt()
的結果,直到比較出大小就停止。比如:
var str1 = "a11"
var str2 = "a2"
// str1 和 str2 比較的時候,會先比較 str1[0] 和 str2[0],兩個都是 "a",比較下一個
// str1[1] 是"1",charCodeAt()是49,str2[1] 是"2",結果是50,所以 str1[1] < str2[1],對比結束
// 最終結果 str1 < str2
同理,在比較"08:00"
和 "09:00"
的時候,先比較兩個"0"
,發現一致之後比較"8"
和"9"
,所以"08:00" < "09:00"
。
這裏有一個問題就是,時間格式必須保持一致,位數不夠的記得補"0"
,拿"8:00"
和"10:00"
比較會發現結果有問題,必須拿"08:00"
和"10:00"
比較纔可以。
這個問題就說到這裏,大家有其他的方法可以留言補充,給大家提供不同的思路。開胃菜結束,進入正題。
正題
作爲一個愛(記)學(不)習(清)的好(笨)孩子,通過字符串比較這件事,我意識到還有更多的非相同類型的比較,比如字符串和數字的比較,布爾和數組的比較(我瘋了麼我這麼用),另外還有加減乘除等其他操作符。
我覺得有必要整理一下了。
我第一反應是這張圖:
真是迷人的笑容呢 :)
在比較之前,我們需要先了解下各種數據類型轉化的結果有哪些。
轉數字
-
字符串:
- 空字符串是0
- 字符串頭尾有空格會忽略
- 空格在中間,或者字符串中含有非數字類型字符,轉換結果就是
NaN
- 布爾:
true -> 1
,false -> 0
- undefined字:
NaN
- null: 0
-
數組:
- 空數組是0
- 如果數組中有且只有一項是數字元素,轉換爲數字
- 其他情況
NaN
-
對象:
- 如果對象有
valueOf()
方法,就調用該方法。如果返回基本類型值,就將這個值轉化爲數字 - 如果對象沒有
valueOf()
方法或者該方法返回的不是基本類型值,就會調用該對象的toString()
方法。如果存在且返回值是基本類型值,就轉化爲數字 - 否則就報錯
- 如果對象有
- 函數:NaN
轉字符串
-
undefined
->"undefined"
-
null
->"null"
-
true
->"true"
/false
->"false"
- 數字:極小和極大的數字使用指數形式,一般的情況你懂得
-
對象:
- 如果對象有
toString()
方法,就調用toString()
方法。如果該方法返回基本類型值,就將這個值轉化爲字符串 - 如果對象沒有
toString()
方法或者該方法返回的不是基本類型值,就會調用該對象的valueOf()
方法。如果存在且返回值是基本類型值,就轉化爲字符串 - 否則就報錯
- 除非自行定義,否則
toString()
返回內部屬性[[Class]]
的值,如"[object Object]"
- 如果對象有
轉布爾
- 所有的假值(
undefined
、null
、+0
、-0
、NaN
、""
)會被轉化爲false
,其他都會被轉爲true
- 所以,空對象、空數組都是
true
轉對象
-
null
和undefined
轉對象直接拋異常 - 基本類型通過調用
String()
、Number()
、Boolean()
構造函數,轉換爲他們各自的包裝對象
使用場景
知道了各種數據類型轉化的規則,那麼在不同的場景中,究竟是怎麼使用的呢?
== 運算符
常見的誤區是:==
檢查值是否相等,===
檢查值和類型是否相等。
正確的解釋是:==
允許在相等比較中進行強制類型轉換,而===
不允許。
事實上,==
和===
都會檢查操作數的類型,區別在於類型不同時它們的處理方式不同。
- 如果一個值是
null
,另一個值是undefined
,則相等 - 如果一個是字符串,另一個值是數字,則把字符串轉換成數字,進行比較
- 如果任意值是
true
,則把true
轉換成1再進行比較;如果任意值是false
,則把false
轉換成0再進行比較 -
如果一個是對象,另一個是數值或字符串,把對象轉換成基礎類型的值再比較
- 對象轉基礎類型時,優先調用
valueOf()
,再調用toString()
。 - 例外的是
Date
,Date
利用的是toString()
轉換
- 對象轉基礎類型時,優先調用
經典題
[] == false // true
!![] // true
//原因是 == 兩邊都轉爲數字進行比較,而不是 [] 轉爲布爾值與 false 比較
+ 運算符
+ 運算符可以作爲一元運算符使用,此時的作用是將後邊跟着的數據轉爲數字
+true // 1
+[] // 0
+new Date() //獲取當前時間的Unix時間戳
在作爲二元運算符使用時,+
運算符比- * /
運算符要複雜一些,因爲其他的運算符都是處理數字的,而+
運算符還可以處理字符串拼接。
- 兩邊如果有字符串,另一邊會轉化爲字符串進行相加
- 如果沒有字符串,兩邊都會轉化爲數字進行相加,對象也根據前面的方法轉化爲數字
- 如果其中的一個操作數是對象,則將對象轉換成原始值,日期對象會通過
toString()
方法進行轉換,其他對象通過valueOf()
方法進行轉換,但是大多數都是不具備可用的valueOf()
方法,所以還是會通過toString()
方法執行轉換
簡單來說就是,如果+
運算符的其中一個操作數是字符串(或者通過以上步驟可以得到字符串),那麼就執行字符串拼接,否則執行數字加法。
經典題
!+[]+[]+![] //"truefalse"
//首先第一個 + 左邊不是數值,所以它是一元運算符,將後邊跟着的 [] 轉化爲數字 0
//同時,最後一個 [] 左邊是 ! 運算符,將 [] 轉化爲布爾值並取反,爲 false
//轉化後的結果爲 !0 + [] + false
//!0 結果爲 true,[] 轉化爲 "",所以結果變爲 true + "" + false
//因爲 第一個 + 右邊有字符串,所以變爲"true" + false
//最終結果爲 "truefalse"
條件判斷
以下條件判斷的語句,會發生隱式轉換爲布爾值的情況:
-
if()
語句中的條件判斷表達式 -
for(..; ..; ..)
語句中的條件判斷表達式 -
while()
和do .. while()
-
? :
中的條件判斷表達式 -
||
和&&
左邊的操作數
補充:valueOf()和toString()
常用內置對象調用toString()
和valueOf()
的返回情況
類型 | toString | valueOf |
---|---|---|
Object | "[object 類型名]" |
對象本身 |
String | 字符串值 | 字符串值 |
Number | 返回數值的字符串表示。還可返回以指定進製表示的字符串,默認10進制 | 數字值 |
Boolean |
"true" / "false"
|
Boolean 值 |
Array | 每個元素轉換爲字符串,用英文逗號作爲分隔符進行拼接 | 數組本身 |
Date | 日期的文本表示,格式爲Wed Jun 05 2019 18:22:32 GMT+0800 (中國標準時間)
|
返回時間戳,等同於調用getTime()
|
Function | 函數的文本表示 | 函數本身 |
RegExp | 正則的文本表示 | 正則本身 |
以上是本篇文章的內容,歡迎大家提出自己的想法,我們一起學習進步,與君共勉。