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

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

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

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