摘要:看下面的代碼和會對操作數(shù)執(zhí)行條件判斷,如果操作數(shù)不是布爾值,會先執(zhí)行類型轉(zhuǎn)換后再執(zhí)行條件判斷。大家記住這個規(guī)則布爾值如果與其他類型進(jìn)行抽象比較,會先用將布爾值轉(zhuǎn)換為數(shù)字再比較。
在上一篇中我們聊過了 JS 類型轉(zhuǎn)換的規(guī)則和我發(fā)現(xiàn)的一些常見書籍中關(guān)于類型轉(zhuǎn)換的一些小錯誤,當(dāng)碰到顯示類型轉(zhuǎn)換的時候大家可以按照這些規(guī)則去拆解出答案。但 JS 中存在一些很隱晦的隱式類型轉(zhuǎn)換,這一篇就來談下我對隱式類型轉(zhuǎn)換的一些總結(jié)。
關(guān)于 JS 類型轉(zhuǎn)換規(guī)則請看上一篇的內(nèi)容:掌握 JS 類型轉(zhuǎn)換:從規(guī)則開始
什么是隱式類型轉(zhuǎn)換呢?顧名思義就是有時候你感覺不到這是類型轉(zhuǎn)換但是實際上類型轉(zhuǎn)換已經(jīng)發(fā)生了。所以這個 "隱式" 取決于我們的理解和經(jīng)驗,如果你看不出來那就是隱式的。
下面按照我自己對于隱式轉(zhuǎn)換的分類來逐個聊聊吧。
一元操作符 +、-var a = "123"; var b = +a; console.log(b); // 123
先來看看 + 或 - 在一個類型值前面,這里會執(zhí)行 ToNumber 類型轉(zhuǎn)換。如果是 - 在前面的話,還會將結(jié)果的符號取反,如:-"123" 的結(jié)果是 -123。并且如果原類型是對象的話也是遵循 ToNumber 的轉(zhuǎn)換規(guī)則,大家可以自己試試,這里就不再舉多余的例子了。
二元操作符接下來我們來看一下二元操作符相關(guān)的隱式轉(zhuǎn)換,比如:+、-、&&、||、==等等這些。
相減 a - bvar a = "123"; var b = true; console.log(a - b); // 122
當(dāng)執(zhí)行減法操作時,兩個值都會先執(zhí)行 ToNumber 轉(zhuǎn)換,所以這個是比較簡單的,當(dāng)類型是對象時也是遵循同樣的規(guī)則。
相加 a + bconsole.log("123" + 4); // "1234" console.log(123 + true); // 124
相加的情況有點復(fù)雜,但隱式轉(zhuǎn)換的規(guī)則大家可以按照我總結(jié)的來記:
如果 + 的操作數(shù)中有對象,則執(zhí)行 ToPrimitive 并且 hint 是 Number
如果 + 中有一個操作數(shù)是字符串(或通過第一步得到字符串),則執(zhí)行字符串拼接(另一個操作數(shù)執(zhí)行 ToString 轉(zhuǎn)換),否則執(zhí)行 ToNumber 轉(zhuǎn)換后相加
這個相加操作的隱式轉(zhuǎn)換規(guī)則看似有點麻煩,其實解析后還是很明確的。
第一步,先看操作數(shù)里面有沒有對象,如果有就是執(zhí)行 hint 是 Number 的 ToPrimitive 操作。大家可以回憶下上篇說的 ToPrimitive 的內(nèi)容,這里要注意的是這里的 ToPrimitive 并沒有將操作數(shù)強(qiáng)制轉(zhuǎn)化為 Number 類型。因為 hint 是 Number,所以先執(zhí)行 valueOf() ,如果返回了字符串那轉(zhuǎn)換結(jié)果就是字符串了;如果返回的不是基本類型值才會執(zhí)行 toString(),如果都沒有返回基本類型值就直接拋異常了。
第二步,如果有一個操作數(shù)是字符串,那么整個結(jié)果就是字符串拼接的,否則就是強(qiáng)轉(zhuǎn)數(shù)字加法;第二個操作數(shù)就會按這個規(guī)則進(jìn)行對應(yīng)的類型轉(zhuǎn)換。
開頭的代碼說明了字符串加數(shù)字、數(shù)字加布爾值的結(jié)果按這個規(guī)則走的,下面我們來看看對象情況下的代碼:
var a = Object.create(null); a.valueOf = function() { return "123"; } a.toString = function() { return "234"; } console.log(a + 6); // "1236"
以上的執(zhí)行結(jié)果說明了執(zhí)行 ToPrimitive 并且 hint 是 Number 結(jié)論是正確的,因為 "123" 是 valueOf 返回的。兩個操作數(shù)相加的其他情況大家也可以自己試試,記住我上面的總結(jié)就完了。
a && b、a || b在 JS 中我們都知道 && 和 || 是一種"短路”寫法,一般我們會用在 if 或 while 等判斷語句中。這一節(jié)我們就來說說 && 和 || 出現(xiàn)的隱式類型轉(zhuǎn)換。
我們通常把 && 和 || 稱為邏輯操作符,但我覺得 《你不知道的 Javascript(中卷)》中有個說法很好:稱它們?yōu)?選擇器運算符"??聪旅娴拇a:
var a = 666; var b = "abc"; var c = null; console.log(a || b); // 666 console.log(a && b); // "abc" console.log(a || b && c); // 666
&& 和 || 會對操作數(shù)執(zhí)行條件判斷,如果操作數(shù)不是布爾值,會先執(zhí)行 ToBoolean 類型轉(zhuǎn)換后再執(zhí)行條件判斷。最后 && 和 || 會返回一個操作數(shù)的值還不是返回布爾值,所以稱之為"選擇器運算符"很合理。
這里有個可能很多人都不知道的情況是:在判斷語句的執(zhí)行上下文中,&& 和 || 的返回值如果不是布爾值,那么還會執(zhí)行一次 ToBoolean 的隱式轉(zhuǎn)換:
var a = 666; var b = "abc"; var c = null; if (a && (b || c)) { console.log("yes"); }
如果要避免最后的隱式轉(zhuǎn)換,我們應(yīng)該這樣寫:
if (!!a && (!!b || !!c)) { console.log("yes"); }a == b 和 a === b
從這里開始是 JS 中隱式轉(zhuǎn)換最容易中坑的地方
首先我們先明確一個規(guī)則:"== 允許在相等比較中進(jìn)行類型轉(zhuǎn)換,而 === 不允許。"
所以如果兩個值的類型不同,那么 === 的結(jié)果肯定就是 false 了,但這里要注意幾個特殊情況:
NaN !== NaN
+0 === -0
ES5 規(guī)范定義了 == 為"抽象相等比較",即是說如果兩個值的類型相同,就只比較值是否相等;如果類型不同,就會執(zhí)行類型轉(zhuǎn)換后再比較。下面我們就來看看各種情況下是如何轉(zhuǎn)換的。
null == undefined這個大家記住就完了,null == undefined // true。也就是說在 == 中 null 與 undefined 是一回事。
所以我們判斷變量的值是 null 或者 undefined 就可以這樣寫了:if (a == null) {...}。
數(shù)字和字符串的抽象相等比較一個操作數(shù)是字符串一個是數(shù)字,則字符串會被轉(zhuǎn)換為數(shù)字后再比較,即是:ToNumber(字符串) == 數(shù)字。
var a = 666; var b = "666"; console.log(a == b); // true布爾值與其他類型的抽象相等比較
注意,這里比較容易犯錯了:
var a = "66"; var b = true; console.log(a == b); // false
雖然 "66" 是一個真值,但是這里的比較結(jié)果卻不是 true,很容易掉坑里。大家記住這個規(guī)則:布爾值如果與其他類型進(jìn)行抽象比較,會先用 ToNumber 將布爾值轉(zhuǎn)換為數(shù)字再比較。
顯然 "66" == 1 的結(jié)果當(dāng)然是 false 咯。
對象與非對象的抽象相等比較先說下規(guī)則:如果對象與非對象比較,則先執(zhí)行 ToPrimitive(對象),并且 hint 參數(shù)為空;然后得到的結(jié)果再與非對象比較。
這里值得注意的是:在 ToPrimitive() 調(diào)用中如果 hint 參數(shù)為空,那么 [[DefaultValue]] 的調(diào)用行為跟 hint 是Number 時一樣——先調(diào)用 valueOf() 不滿足條件再調(diào)用 toString()。
注意這里有個例外情況:如果對象是 Date 類型,則 [[DefaultValue]] 的調(diào)用行為跟 hint 是 String 時一樣。
我們來測試一下是不是這樣的:
var a = Object.create(null); a.valueOf = function() { console.log("a.valueOf is invoking."); return 666; }; a.toString = function() { console.log("a.toString is invoking."); return "666"; }; console.log(a == 666); // a.valueOf is invoking. // true console.log(a == "456"); // a.valueOf is invoking. // false a.valueOf = undefined; console.log(a == "666"); // a.toString is invoking. // true
根據(jù)輸出來看依據(jù)上面的規(guī)則來解釋是 OK 的。
有一個開源項目有張圖表可以方便大家去記憶 == 與 ===,點擊 這里 查看。a > b、a < b
按慣例先總結(jié)規(guī)則,情況略微復(fù)雜:
第一步:如果操作數(shù)是對象則執(zhí)行 ToPrimitive(對象),并且 hint 參數(shù)為空。
第二步:
如果雙方出現(xiàn)非字符串,則對非字符串執(zhí)行 ToNumber,然后再比較
如果比較雙方都是字符串,則按字母順序進(jìn)行比較
我們還是用代碼來測試下:
var a = Object.create(null); a.valueOf = function() { console.log("a.valueOf is invoking."); return "666"; }; a.toString = function() { console.log("a.toString is invoking."); return true; }; console.log(a > "700"); // a.valueOf is invoking. // false a.valueOf = undefined; console.log(a < 2); // a.toString is invoking. // true
這里注意下當(dāng)測試 a < 2 時,toString() 返回了 true,然后會執(zhí)行 ToNumber(true) 返回 1,最后 1 < 2 的結(jié)果就是 true。
a ≥ b,a ≤ b最后這里也是一個比較容易中坑的地方。
根據(jù)規(guī)范 a ≤ b 會被處理為 a > b,然后將結(jié)果反轉(zhuǎn),即處理為 !(a > b);a ≥ b 同理按照 !(a < b) 處理。
我們來看個例子:
var a = { x: 666 }; var b = { x: 666 }; console.log(a >= b); // true console.log(a <= b); // true
這里 a 和 b 都是字面量對象,valueOf() 的結(jié)果還是對象,所以轉(zhuǎn)為執(zhí)行 toString(),結(jié)果都是"[object Object]",當(dāng)然 a < b 和 a > b 的結(jié)果都是 false,然后取反結(jié)果就是 true 了?!?和 ≥ 的結(jié)果都是 true,是不是有點出乎意料呢
總結(jié)上一篇寫了 JS 類型轉(zhuǎn)換的規(guī)則,這一篇寫了隱式轉(zhuǎn)換中我總結(jié)的經(jīng)驗和判斷法則。感覺已經(jīng)差不多了,剩下的就是實踐中自己去理解了,后續(xù)可能還會找一些比較坑的類型轉(zhuǎn)換示例代碼寫一篇拆解分析。
感謝大家花時間聽我比比,歡迎 star 和關(guān)注我的 JS 博客:小聲比比 Javascript
參考資料ES5 規(guī)范注釋
《你不知道的 Javascript(中卷)》
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/104662.html
摘要:下面先看看涉及到的幾個函數(shù)以及他們的轉(zhuǎn)換規(guī)則,這個是需要記憶的內(nèi)容類型轉(zhuǎn)換需要使用到的函數(shù)對于布爾值用到的是對于數(shù)值,用到的是當(dāng)然還有但是對于隱式類型轉(zhuǎn)換的時候,調(diào)用的是前者。 javaScript類型轉(zhuǎn)換規(guī)則 javaScript的類型轉(zhuǎn)換其實一直是很多前端開發(fā)人員很迷的地方,一會兒這里要轉(zhuǎn)換,一會兒那里又要轉(zhuǎn)換,總之就是一個大寫的迷,因為它隱式類型轉(zhuǎn)換的地方實在是太多了。 但其實...
摘要:結(jié)合實際中的情況來看,有意或無意中涉及到隱式類型轉(zhuǎn)換的情況還是很多的。此外當(dāng)進(jìn)行某些操作時,變量可以進(jìn)行類型轉(zhuǎn)換,我們主動進(jìn)行的就是顯式類型轉(zhuǎn)換,另一種就是隱式類型轉(zhuǎn)換了。 前言 相信剛開始了解js的時候,都會遇到 2 ==2,但 1+2 == 1+2為false的情況。這時候應(yīng)該會是一臉懵逼的狀態(tài),不得不感慨js弱類型的靈活讓人發(fā)指,隱式類型轉(zhuǎn)換就是這么猝不及防。結(jié)合實際中的情況來看...
摘要:當(dāng)一個值為字符串,另一個值為非字符串,則后者轉(zhuǎn)為字符串。文章出自的個人博客 showImg(https://segmentfault.com/img/bVEWkS?w=3376&h=1312); JavaScript 是一門弱類型語言,剛接觸的時候感覺方便快捷(不需要聲明變量類型了耶?。?,接觸久了會發(fā)現(xiàn)它帶來的麻煩有的時候不在預(yù)期之內(nèi) 呵呵一笑,哪有這么夸張,可能有人看過這樣一段代碼 ...
摘要:首先,為了掌握好類型轉(zhuǎn)換,我們要理解一個重要的抽象操作為什么說這是個抽象操作呢因為這是內(nèi)部才會使用的操作,我們不會顯示調(diào)用到?;疽?guī)則中的類型轉(zhuǎn)換總是返回基本類型值,如字符串?dāng)?shù)字和布爾值,不會返回對象和函數(shù)。 Javascript 里的類型轉(zhuǎn)換是一個你永遠(yuǎn)繞不開的話題,不管你是在面試中還是工作寫代碼,總會碰到這類問題和各種的坑,所以不學(xué)好這個那是不行滴。關(guān)于類型轉(zhuǎn)換我也看過不少的書和各...
摘要:本文從底層原理到實際應(yīng)用詳細(xì)介紹了中的變量和類型相關(guān)知識。內(nèi)存空間又被分為兩種,棧內(nèi)存與堆內(nèi)存。一個值能作為對象屬性的標(biāo)識符這是該數(shù)據(jù)類型僅有的目的。 導(dǎo)讀 變量和類型是學(xué)習(xí)JavaScript最先接觸到的東西,但是往往看起來最簡單的東西往往還隱藏著很多你不了解、或者容易犯錯的知識,比如下面幾個問題: JavaScript中的變量在內(nèi)存中的具體存儲形式是什么? 0.1+0.2為什...
閱讀 1760·2023-04-26 00:30
閱讀 3180·2021-11-25 09:43
閱讀 2915·2021-11-22 14:56
閱讀 3218·2021-11-04 16:15
閱讀 1188·2021-09-07 09:58
閱讀 2052·2019-08-29 13:14
閱讀 3138·2019-08-29 12:55
閱讀 1019·2019-08-29 10:57