聊一聊 JS 中的『隱式類型轉(zhuǎn)換』

轉(zhuǎn)載:https://segmentfault.com/a/1190000004482388


類型轉(zhuǎn)換還不行?還非得隱式?這是什么高級玩意?

廢話不多說,我們先上一盤?,額,不對,先看一個例子吧。

3?+ true

實際上在大多數(shù)編程語言中,都會認為上面這個表達式是錯誤的。因為布爾表達式與算術運算是不兼容的。尤其是在靜態(tài)語言中,甚至不會被運行運行。即使是動態(tài)語言中,通常雖然可以讓程序運行,但是會拋出一個異常。

然而,然而, Javascript 不僅運行程序能夠正常運行,而且還會順利地產(chǎn)生結(jié)果 4。Javascript 真的是對類型錯誤出奇的寬容啊??雌饋砗芟袷且患檬聦Σ粚Γ?/p>

基本上,在 Javascript 中,只有在一些極少數(shù)情況下才會因為類型錯誤而拋出一個異常。諸如: 調(diào)用非函數(shù)對象或者獲取 null / undefined 的屬性時。

但是在大多數(shù)情況下,Javascript 都是不會拋出異常的。這個『小婊砸』反而按照多種多樣的轉(zhuǎn)換協(xié)議偷偷的強制轉(zhuǎn)換為她期望的值。諾,你看,還花樣轉(zhuǎn)換呢,真會玩嘛。這就是所謂的『隱式類型轉(zhuǎn)換』。

那么,上面那個例子中,究竟是發(fā)生了什么樣的轉(zhuǎn)換方式呢?

首先,Javascript 這個『小婊砸』在遇到算數(shù)運算符(-?、*?、/?和?%)的時候會在運算之前將參與運算的雙方轉(zhuǎn)換成數(shù)字。

那么問題又來了,true?怎么就轉(zhuǎn)換成數(shù)字了呢?實際上我們通過?Number(true)?就可以看到,?true?轉(zhuǎn)換為數(shù)字之后就是為 1,相反,false?轉(zhuǎn)換為數(shù)字之后就對應為 0。

細心的你可能發(fā)現(xiàn)我在上面并沒有提到?+?運算符,那是因為它更復雜。因為它既承擔著數(shù)字相加,又肩負著字符串連接操作的重任。具體的行為取決于參數(shù)的類型。

但是,如果一個數(shù)字和一個字符串相加,會碰撞出什么樣的火花呢?

顯然 Javascript 這個『小婊砸』更偏愛字符串多一點,她會將數(shù)字(toString())轉(zhuǎn)換為字符串,然后執(zhí)行字符串連接操作。

例如:

"1"+2;? ? //"12"1 +"2";? ? //"12"

但是,注意,Javascript 對操作順序非常敏感,以至于會發(fā)生這樣的事情:

1?+?2?+?"3"; //?"33"

因為加法運算是自左向右的,因此它等同于下面的表達式:

(1?+?2) +?"3";?// "33"

再來看這一個例子:

if(1==true) {? ? alert("true");}else{? ? alert("false");}

相信你一定輕松的猜到了結(jié)果對不對?

但是,哼,你以為我的問題會這么簡單么?那豈不是太小看你了。

我們都知道,Javascript 中,數(shù)字?0?為假,非0?均為真, 那么我想問的是,在上面的條件語句中,到底是?1?被隱式類型轉(zhuǎn)換了呢還是?true?被隱式類型轉(zhuǎn)換了呢?

實際上在條件判斷運算?==?中的轉(zhuǎn)換規(guī)則是這樣的:

如果比較的兩者中有布爾值(Boolean),會把?Boolean?先轉(zhuǎn)換為對應的 Number,即 0 和 1,然后進行比較。

如果比較的雙方中有一方為?Number,一方為?String時,會把?String?通過?Number()?方法轉(zhuǎn)換為數(shù)字,然后進行比較。

如果比較的雙方中有一方為?Boolean,一方為?String時,會將雙方轉(zhuǎn)換為數(shù)字,然后再進行比較。

如果比較的雙方中有一方為?Number,一方為Object時,則會調(diào)用?valueOf?方法將Object轉(zhuǎn)換為數(shù)字,然后進行比較。

例如:

1 == { valueOf: function() {return1;} }? ? // true1 + { valueOf: function() {return1;} }? ? //2

需要強調(diào)的是,在 Javascript 中,只有?空字符串、數(shù)字0、false、null、undefined?和?NaN?這 6 個值為假之外,其他所有的值均為真值。

說到?NaN,就不得不提一下?isNaN()?方法,isNaN()?方法自帶隱式類型轉(zhuǎn)換,該方法在測試其參數(shù)之前,會先調(diào)用Number()?方法將其轉(zhuǎn)換為數(shù)字。所以?isNaN('1')?這個語句中明明用一個字符串去測試,返回值仍然為?false?也就不足為怪了。

在?+?號運算中還有一種更復雜的情況,那就是數(shù)字/字符串和對象進行運算的時候,上面已經(jīng)舉例說明了數(shù)字和對象運算的情況,我們再來說一下字符串和對象運算的情況。

當字符串和對象進行?+?運算的時候,Javascript 會通過對象的?toString()?方法將其自身轉(zhuǎn)換為字符串,然后進行連接操作。

"1"?+ { toString:?function()?{return?1;} }?// "11"

之所以說它特殊,是因為當一個對象同時包含?toString()?和?valueOf()?方法的時候,運算符?+?應該調(diào)用哪個方法并不明顯(做字符串連接還是加法應該根據(jù)其參數(shù)類型,但是由于隱式類型轉(zhuǎn)換的存在,類型并不顯而易見。),Javascript 會盲目的選擇?valueOf()?方法而不是?toString()?來解決這個問題。這就意味著如果你打算對一個對象做字符串連接的操作,但結(jié)果卻是......

varobj = {toString:function(){return"Object CustomObj"; },valueOf:function(){return1; }};console.log("Object: "+ obj);// "Object: 1"

隱式類型轉(zhuǎn)換會給我們造成很多麻煩,那么該怎么避免呢?

建議在所有使用條件判斷的時候都使用全等運算符?===?來進行條件判斷。全等運算符會先進行數(shù)據(jù)類型判斷,并且不會發(fā)生隱式類型轉(zhuǎn)換。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容