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