iT邦幫忙

0

為了轉生而點技能-JavaScript,難題紀錄(二)隱式轉換規則及===、==

隱式轉換規則

前言:

涉及隱式轉換最多的兩個運算子 + 和 ==。
+運算子即可數字相加,也可以字串相加。
== 不同於===,故也存在隱式轉換。
減、乘、除這類運算子只會針對number型別,故轉換的結果就是轉換成number型別。


轉換表

https://ithelp.ithome.com.tw/upload/images/20211123/20143762yUTVcyol3N.png


規則:

第一步:決定該轉換成什麼類型:

加號(+)專屬規則:

  1. 當一側為String類型,加號(+)會被定義為字元串接,並會讓另一側優先轉換為字串。
  2. 當一側為Number類型,另一側為原始類型(primiary type),原始類型會轉換為Number型。
  3. 當一側為Number類型,另一側為引用類型(reference type:Array , Function , Object ),引用類型(一側)和Number型(另一側)皆轉換成字串後串接。

優先順序為由上而下!

第二步:需轉換側的值該如何轉。

  1. 將值轉為原始值,ToPrimitive():
    js引擎內部的抽象操作ToPrimitive有著這樣的簽名:ToPrimitive(input, PreferredType?)
    input是要轉換的值,PreferredType是可選引數,可以是Number或String型別,由第一步決定。

2-1. PreferredType是Number型(第一步決定該轉換為Number型)時:

  • 如果輸入的值已經是一個原始值,則直接返回它
  • 否則,如果輸入的值是一個物件,則呼叫該物件的valueOf()方法,如果valueOf()方法的返回值是一個原始值,則返回這個原始值。
  • 否則,呼叫這個物件的toString()方法,如果toString()方法返回的是一個原始值,則返回這個原始值。
  • 否則,丟擲TypeError異常。

2-2. PreferredType是String型(第一步決定該轉換為String型)時:

  • 如果輸入的值已經是一個原始值,則直接返回它
  • 否則,呼叫這個物件的toString()方法,如果toString()方法返回的是一個原始值,則返回這個原始值。
  • 否則,如果輸入的值是一個物件,則呼叫該物件的valueOf()方法,如果valueOf()方法的返回值是一個原始值,則返回這個原始值。
  • 否則,丟擲TypeError異常。

2-3. PreferredType在第一步沒有辦法指定轉換哪一種型時:


3-1.valueOf()方法:

  • Number、Boolean、String這三種建構函式生成的基礎值的物件形式,通過valueOf轉換後會變成相應的原始值。
  • Date這種特殊的物件,其原型Date.prototype上內建的valueOf函式將日期轉換為日期的毫秒的形式的數值。
  • 除此之外返回的都為this,即物件本身。
var num = new Number('123');
num.valueOf(); // 123

var str = new String('12df');
str.valueOf(); // '12df'

var bool = new Boolean('fd');
bool.valueOf(); // true

var a = new Date();
a.valueOf(); // 1515143895500

var a = new Array();
a.valueOf() === a; // true

var b = new Object({});
b.valueOf() === b; // true

3-2.toString:

  • Number、Boolean、String、Array、Date、RegExp、Function這幾種建構函式生成的物件,通過toString轉換後會變成相應的字串的形式。
  • 其他物件返回的都是該物件的型別。
  • 參考轉換表。

第三步:原始值轉換。

經過前面步驟,會得到一個原始值,之後依照運算子的需求選擇利用ToNumber將該原始值轉換成數自型態;或是利用ToString將該原始值轉換為字串,轉換規則參考頂層的轉換表。


試解:

第一題:

({} + {}) = ?
兩個物件的值進行+運算子,肯定要先進行隱式轉換為原始型別才能進行計算。

  1. 第一步:決定該轉換成什麼類型:因為不屬於+號的3個條件,屬於2-3.之情況。
    1、第二步:ToPrimitive轉換,由於沒有指定PreferredType型別,{}會使預設值為Number,進行ToPrimitive(input, Number)運算。
    2、所以會執行valueOf方法,({}).valueOf(),返回的還是{}物件,不是原始值。
    3、繼續執行toString方法,({}).toString(),返回"[object Object]",是原始值。
    故得到最終的結果,"[object Object]" + "[object Object]" = "[object Object][object Object]"

第二題:

2 * {} = ?
1、首先乘號運算子只能對number型別進行運算,故第一步就是對{}進行ToNumber型別轉換。
2、由於{}是物件型別,故先進行原始型別轉換,ToPrimitive(input, Number)運算。
3、所以會執行valueOf方法,({}).valueOf(),返回的還是{}物件,不是原始值。
4、繼續執行toString方法,({}).toString(),返回"[object Object]",是原始值。
5、轉換為原始值後再進行ToNumber運算,"[object Object]"就轉換為NaN。
故最終的結果為 2 * NaN = NaN



嚴格相等(===)

定義:比較兩側的值是否在型態上及數值上皆相同。

NaN === NaN;  //false
+0 === -0;    //true

寬鬆相等(==)

定義:寬鬆相等不限於型態相同,即型態不相同也可寬鬆相等。

1 == '1' // true

運作方式:

  1. 一側為boolean值的寬鬆相等比較:
    值首先會被轉換為數字類型,根據boolean類型的ToNumber規則,true轉為1,false轉為0。
  2. 數字類型和字串類型的寬鬆相等比較:
    當數字類型和字串類型做寬鬆相等比較時,字串類型會被轉換為數字類型;根據字串的ToNumber規則,如果是純數字形式的字符串,則轉為對應的數字,空字符轉為0, 否則一律按轉換失敗處理,轉為NaN。
    注意NaN不與任何值相等,包括它自己。
    注意字串內如果是16進位制,也算純數字。
  3. 當引用型別和原始型別的寬鬆相等比較:
    對象類型會依照ToPrimitive規則轉換為原始類型

試解:

第一題:

[2] == 2 // true

解:

  1. 屬於引用型別和原始型別的寬鬆相等比較。
  2. [2]做ToPrimitive操作,因為[2]非data型(2-3.),所以PreferredType是Number型
  3. [2]也就是先調用valueOf,回傳仍是[2]。
  4. [2]再調用toString,回傳是'2'。
  5. '2'已經符合數字類型和字串類型的寬鬆相等比較之條件,ToNumber變成2。

第二題:

[] == !{} // true

1、! 運算子優先順序高於==,故先進行!運算。
2、!{}運算結果為false,結果變成 [] == false比較。
3、屬於一側為boolean值的寬鬆相等比較:
等式右邊y = ToNumber(false) = 0。結果變成 [] == 0。
4、屬於當引用型別和原始型別的寬鬆相等比較:
[]會先呼叫valueOf函式,返回this。
不是原始值,繼續呼叫toString方法,x = [].toString() = ''。
故結果為 '' == 0。
5、屬於數字類型和字串類型的寬鬆相等比較:
等式左邊x = ToNumber('') = 0。
所以結果變為: 0 == 0,返回true,比較結束。



參考文章:

  1. IT人-javascript 隱式轉換:https://iter01.com/472967.html
  2. 程式前沿-從一道面試題說起—js隱式轉換踩坑合集:https://codertw.com/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80/701003/#outline__1
  3. JavaScript 隐式类型转换,一篇就够了!:https://chinese.freecodecamp.org/news/javascript-implicit-type-conversion/

尚未有邦友留言

立即登入留言