JavaScript那些不經(jīng)意間發(fā)生的數(shù)據(jù)類型自動轉(zhuǎn)換
JavaScript可以自由的進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換,也提供了多種顯式轉(zhuǎn)換的方式。但是更多的情況下,是由JavaScript自動轉(zhuǎn)換的,當(dāng)然這些轉(zhuǎn)換遵循著一定的規(guī)則,了解數(shù)據(jù)類型自由轉(zhuǎn)換的規(guī)則是非常必要的。
數(shù)據(jù)類型
聊到數(shù)據(jù)類型轉(zhuǎn)換,就不得不提到 JavaScript 的數(shù)據(jù)類型:原始類型( Number
, String
, Boolean
, undefined
, null
, Symbol
),對象類型 (Object
)。當(dāng)然Object有可以細(xì)分出 Array
, Date
, RegExp
等等對象。
既然分為這么多種數(shù)據(jù)類型,每種數(shù)據(jù)類型肯定會有特定的用途,那么當(dāng)提供的值的數(shù)據(jù)類型與預(yù)期不符時要怎么辦呢?
比如我需要在控制語句中使用到 boolean
值,提供的值卻是 string
。當(dāng)然我們可以顯式轉(zhuǎn)換 Boolean( a : string)
,但是根據(jù)日常經(jīng)驗,我們知道其實不需要這么復(fù)雜,可以在控制語句中直接用這個string類型的變量 ,也可以達(dá)到預(yù)期的效果。如下:
可見自動轉(zhuǎn)換方便很多,但是在這個過程中到底是按照什么規(guī)則處理的呢?
自動轉(zhuǎn)換
什么時候會發(fā)生自動轉(zhuǎn)換?
犀牛書上是這樣描述的: 當(dāng)JavaScript期望使用一個布爾值的時候,你可以提供任意類型值,JavaScript將根據(jù)需要自行轉(zhuǎn)換類型。一些值(真值)轉(zhuǎn)換為true , 一些值(假值)轉(zhuǎn)換為false 。這在其他類型中同樣適用:如果JavaScript期望使用一個字符串,它把給定的值轉(zhuǎn)換為字符串。如果JavaScript期望一個數(shù)字,它把給定的值轉(zhuǎn)換為數(shù)字(如果轉(zhuǎn)換結(jié)果毫無意義的話,將會返回NaN)。
簡而言之就是:JavaScript有一些語句/運算符對數(shù)據(jù)類型有要求,但我們提供的與預(yù)期不符時,就會發(fā)生自動類型轉(zhuǎn)換。
對數(shù)據(jù)類型有期待的表達(dá)式和運算符
- 期待boolean類型的:
if
、do while
、while do
、&&
、||
、!
(與或非邏輯表達(dá)式) 、? :
( 三目運算符) - 期待number類型的 :
+
-
*
/
%
(算數(shù)運算符) 、++
--
(增量/減量運算符) 、>
>=
<
<=
(數(shù)字比較) - 期待string的:
+
(字符串連接) 、>
>=
<
<=
(字母排序比較) - 特殊的 :
==
、!=
(不)相等運算符,在檢測兩個操作數(shù)是否相等時,會進(jìn)行類型轉(zhuǎn)換;(注意 :===
、!==
是(不)嚴(yán)格相等運算符,是不會進(jìn)行類型轉(zhuǎn)換的)
需要說明的是,1中當(dāng)然可以傳入表達(dá)式,但是表達(dá)式返回的結(jié)果也肯定會返回boolean
類型的值,或者返回值被轉(zhuǎn)換為boolean
;2和3有一些重復(fù)的運算符 : +
、 >
、 >=
、 <
、 <=
,這些運算符在不同場景下發(fā)生自動轉(zhuǎn)換的時候,會有不同的優(yōu)先級。
運算符在不同場景的轉(zhuǎn)換優(yōu)先級
+
// + 有兩種作用:算數(shù)運算和字符串連接。所以期待的是數(shù)字和字符串! // 1、兩個操作數(shù)同為數(shù)字,或者同為字符串,不需要進(jìn)行轉(zhuǎn)換 1 + 1 // 2 '1' + '1' // '11' // 2、有一個操作數(shù)是字符串,則另外一個也會轉(zhuǎn)換為字符串 '1' + 1 // "11" '1' + null // "1null" '1' + {} // "1[object Object]" '1' + new Date() // "1Wed Jun 20 2018 11:49:55 GMT+0800 (中國標(biāo)準(zhǔn)時間)" // 3、如果有一個操作數(shù)是對象的話,會轉(zhuǎn)化為原始值(除了Date對象是調(diào)用toString()方式轉(zhuǎn)換 , 其他對象都會調(diào)用 valueOf() 進(jìn)行轉(zhuǎn)換 , 但是由于多數(shù)對象只是簡單的繼承了valueOf() , 只會返回對象,而不是一個原始值,所以會再調(diào)用toString進(jìn)行轉(zhuǎn)換) , 所以這里可以簡單的理解為:都會轉(zhuǎn)換為字符串 。 另一個操作數(shù)也會轉(zhuǎn)換為字符串 1 + {} // "1[object Object]" 1 + new Date() // "1Wed Jun 20 2018 11:56:56 GMT+0800 (中國標(biāo)準(zhǔn)時間)" 1 + [] // "1" // 4、 其他情況都會轉(zhuǎn)換為數(shù)字 1 + null // 1 1 + undefined // NaN
從例子中可以看到,+
的轉(zhuǎn)換其實是優(yōu)先轉(zhuǎn)換為字符串的,如果操作數(shù)中又字符串或者對象(對象在這里會轉(zhuǎn)換為字符串),則會按照 字符串連接進(jìn)行操作的。從例子的第3個可以看到,第一個操作數(shù)都是數(shù)字,但是會轉(zhuǎn)化為字符串。例子中的第4個,沒有對象和字符串,null
和 undefined
都轉(zhuǎn)換為 數(shù)字進(jìn)行算數(shù)運算,其中 undefined -> number
會返回 NaN
, 所以計算結(jié)果為 NaN
。
+
還有特殊的用法,就是轉(zhuǎn)換為數(shù)字,如下。會將 +
后面的操作數(shù)轉(zhuǎn)換為數(shù)字,具體的轉(zhuǎn)換規(guī)則后續(xù)會說明。
+ null // 0 + undefined // NaN + {} // NaN + new Date() // 1529467646682 + ["5"] // 5 + "4" // 4
> 、>= 、< 、<=
> 、>= 、< 、<=
這些比較運算符的規(guī)則和 +
類似,不過是會優(yōu)先轉(zhuǎn)換為數(shù)字進(jìn)行比較。
// 作用 : 比較數(shù)值大小或者再字母表中的位置。也是期待數(shù)字和字符串! 1、兩個操作數(shù)中只要有一個不是字符串,則兩個值都轉(zhuǎn)為數(shù)字 "3" > "11" // true 字符串比較 3 > "11" // false 11 轉(zhuǎn)換為數(shù)字 true > '0' // true true 和 ‘0' 都轉(zhuǎn)換為數(shù)字 2、對象同樣會轉(zhuǎn)換為原始值(不過這里的Date對象也是優(yōu)先調(diào)用valueOf ,返回的是毫秒數(shù),其他的和上述 + 的一樣),如果轉(zhuǎn)換后有一個字不是字符串,則兩個值也都需要轉(zhuǎn)換為數(shù)字 1000 > new Date() // false 100000000000000000000000 > new Date() // true date對象轉(zhuǎn)換為數(shù)字 "100000000000000000000000" > new Date() // true 左值也隨著 date對象一起轉(zhuǎn)換為數(shù)字 '11' > ["3"] // false 數(shù)組轉(zhuǎn)為字符串,所以這里是字符串比較
這里需要注意的是,只要在轉(zhuǎn)換為數(shù)字的過程中,有一個值是 NaN
,那么比較的結(jié)果肯定是 false
。
== 、 !=
== 、 !=
(不)相等運算符是不嚴(yán)格的比較,所以,如果兩個操作數(shù)不是同一類型,那么會嘗試進(jìn)行一些類型轉(zhuǎn)換,然后進(jìn)行比較。有以下規(guī)則和類型轉(zhuǎn)換:
- 一個值是
undefined
,一個值是null
,則相等 - 一個值是數(shù)字,一個值是字符串,則字符串轉(zhuǎn)換為數(shù)字進(jìn)行比較
true
和false
會分別轉(zhuǎn)換為1
和0
- 一個值是字符串或者數(shù)字,另一個是對象,對象轉(zhuǎn)換為原始值(
Date
類只調(diào)用toString
,其他的和之前的一致),然后進(jìn)行比較。 - 其他的比較,全是
false
。
null == undefined // true 1 null == 0 // false 5 1 == '1' // true 2 1 == true // true 3 2 == true // false 3 1 == [1] // true 4 '1' == ['1'] // true 4 數(shù)組轉(zhuǎn)為字符串 1 == ['1'] // true 4 數(shù)組轉(zhuǎn)為字符串再轉(zhuǎn)為數(shù)字
對象包裝
還有一種自動轉(zhuǎn)換也很容易被忽略,但是經(jīng)常見到。那就是對象包裝。
思考一個問題,為什么數(shù)字是原始類型,卻可以使用 toString
方法? 只有對象才會有方法的,為什么數(shù)字卻可以使用?
let x = 1 x.toString() // "1"
因為在x需要使用方法的時候,JavaScript會通過調(diào)用 new Number(x)
的方式將它暫時轉(zhuǎn)換為對象,它繼承了Numbe
r對象的方法,所以就可以調(diào)用toString
了。同樣的還有字符串、布爾值,也是通過各種的構(gòu)造函數(shù)進(jìn)行轉(zhuǎn)換。這也是為什么undefined
和null
,不可以使用toString
的原因,因為它們沒有構(gòu)造函數(shù)。
x = null x.toString() //VM289:1 Uncaught TypeError: Cannot read property 'toString' of //null // at <anonymous>:1:3 //(anonymous) @ VM289:1 x = undefined x.toString() //VM305:1 Uncaught TypeError: Cannot read property 'toString' of //undefined // at <anonymous>:1:3
目前我所了解的自動轉(zhuǎn)換就只有這么多,后續(xù)再繼續(xù)補(bǔ)充。那么自動轉(zhuǎn)換的過程中,又有哪些規(guī)則呢?
自動轉(zhuǎn)換規(guī)則
any -> boolean
在其他類型的值轉(zhuǎn)換為 boolean
時,只有這幾個會轉(zhuǎn)換為 false
,其他都是 true
: undefined
、 null
、 ""
、 0
、-0
、NaN
。
Boolean(0) // false Boolean("") //false Boolean(NaN) //false Boolean(undefined) //false Boolean(null) // false // 空對象 空數(shù)組 空函數(shù) 都會true Boolean({}) // true Boolean([]) //true Boolean(function () {}) // true // 此時是一個boolean對象,而不是原始值,所以是true Boolean(new Boolean(false)) // true
any -> number
在其他類型的值轉(zhuǎn)換為number是,就復(fù)雜一些:
1.boolean -> number
true -> 1
false -> 0
2.string -> number
由數(shù)字組成的字符串,可以直接轉(zhuǎn)換為數(shù)字,開始和結(jié)尾的空格都可以忽略。不符合的字符串會返回NaN。
+'' // 0 空字符串 +'100' // 100 +' 100 ' // 100 忽略前后空格 +' 100aa' // NaN 有其他非數(shù)字
備注:這里的規(guī)則是自動轉(zhuǎn)換的規(guī)則,如果是顯示轉(zhuǎn)換的話,構(gòu)造函數(shù)Number()
和此規(guī)則一致,而window.parseInt()
window.parseFloat
的解析規(guī)則則不一樣。如下
window.parseInt(' 100a ') // 100 window.parseFloat(' 100.11a') // 100.11
3.對象 -> number
對象會先嘗試調(diào)用 valueOf
返回原始值,如果沒有則調(diào)用toString返回原始值,再進(jìn)行轉(zhuǎn)換返回??磶讉€例子
+new Date() // 1529483712712 date對象的valueOf返回毫米數(shù),即為數(shù)字 +[] // 0 數(shù)組valueOf為它自己,再調(diào)用toString 返回 “” ,空字符串轉(zhuǎn)換為數(shù)字為0 +['1'] // 1 同樣toString 返回 “1” , 轉(zhuǎn)換為數(shù)字為 1 +['1','2'] // NaN toString 返回 “1,2” 轉(zhuǎn)換為數(shù)字 NaN +{} // NaN toString [Object,Object] , 轉(zhuǎn)換為數(shù)字 NaN
4.undefined null
null -> 0
undefined -> NaN
any -> string
1.null undefined boolean number
這幾個原始類型的轉(zhuǎn)換非常簡單,就是將自身用引號包裹而已。
'' + 1 // "1" '' + true // "true" '' + undefined // "undefined" '' + null // "null"
2.object -> string
和對象轉(zhuǎn)化為數(shù)字類似,不過是先調(diào)用toString
,在調(diào)用valueOf
。
'' + {} // "[object Object]" '' + [] // "" '' + [1,2,3] // "1,2,3" '' + function() {} // "function () {}" '' + new Date() // "Wed Jun 20 2018 16:50:56 GMT+0800 (中國標(biāo)準(zhǔn)時間)"
可以看出不同的對象差別挺大的,數(shù)組會將每個元素用逗號分開,生成字符串,date對象toString返回的是中國標(biāo)準(zhǔn)時間,從這里就可以看過和轉(zhuǎn)化成數(shù)字的不同邏輯了,先嘗試 toString
不行才再 valueOf
。
總結(jié)
自動類型轉(zhuǎn)換真的非常常見,常用的一些便捷的轉(zhuǎn)類型的方式,都是依靠自動轉(zhuǎn)換產(chǎn)生的。比如 轉(zhuǎn)數(shù)字 + x
、 x - 0
,轉(zhuǎn)字符串 : "" + x
等等?,F(xiàn)在總算知道為什么可以這樣便捷轉(zhuǎn)換。
以上就是JavaScript那些不經(jīng)意間發(fā)生的數(shù)據(jù)類型自動轉(zhuǎn)換的詳細(xì)內(nèi)容,更多關(guān)于JavaScript數(shù)據(jù)類型轉(zhuǎn)換的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript字符串對象charAt方法入門實例(用于取得指定位置的字符)
這篇文章主要介紹了JavaScript字符串對象charAt方法入門實例,charAt方法用于取得指定位置的字符,需要的朋友可以參考下2014-10-10

Three.js源碼閱讀筆記(基礎(chǔ)的核心Core對象)

JavaScript DOM學(xué)習(xí)第八章 表單錯誤提示

Javascript學(xué)習(xí)筆記之?dāng)?shù)組的構(gòu)造函數(shù)