JS的隱式轉換 從 [] ==false 說起

nanchenk發表於2017-12-04

JS的隱式轉換 從 [] ==false 說起

前言

最近和大創扯淡時說到了[] == false,從結果上來看我倆都答錯了,從氣勢上來說我倆的歪理都能出書了(恩,程式猿的驕傲),但是這其實背後隱藏了一潭很深的水,對,很深。。。


隱式型別轉換

JS的資料型別

首先,回想一下JS的型別都有什麼。

原始值(primitives): undefined, null, booleans, numbers,strings, symbol(es6)

物件值(objects): Object

ok, 這就是全部了,我們接下來看看到底發生了什麼導致隱式轉換如此不可捉摸。

ToPrimitive

在發生轉換的時候,js其實都是會將操作物件轉化為原始的物件,這也是最為詬病的地方,因為js很難直接丟擲錯誤,她會用一套自己的方法去理解我們的錯誤,並做相應的調整,哪怕這些錯誤我們是無意識的。所以我們要知道她的轉換方式,才能做到知己知彼,對程式碼的控制更為精準。

簽名:ToPrimitive(input, PreferredType?) //PreferredType: Number 或者 String

流程如下:

  1. input為原始值,直接返回;
  2. 不是原始值,呼叫該物件的valueOf()方法,如果結果是原始值,返回原始值;
  3. 呼叫valueOf()不是原始值,呼叫此物件的toString()方法,如果結果為原始值,返回原始值;
  4. 如果返回的不是原始值,丟擲異常TypeError。

其中PreferredType控制線調取valueOf()還是toString()。

ps: Date型別按照String去呼叫。

ok,通過這個隱式裝箱,我們得到了運算元的原始值。接下來,我們根據不同情況,看看發生了什麼呢~

數學運算

想必大家用過以下做法去完成型別轉換吧

var str = '1';
var num = str - 0;

var num = 2;
var str = num + '';複製程式碼

這種類似的數學運算會做型別轉換,但是‘+’尤為致命,為啥捏?

當'+'作為雙目運算子時,如a+b。

它的執行如下:

  1. 計算兩個運算元的原始值: prima = ToPrimitive(a), prima = ToPrimitive(b);
  2. 如果原始值有String,全部轉換為String,返回String相加後的結果;
  3. 如果原始值沒有String,全部轉換為Number, 返回Number相加後的結果;

當'+'作為單目運算子時, 例如 +a.

流程是這樣的:

  1. 將a轉換為Number,Number(a);

舉個栗子:

[] + []

複製程式碼
1. 轉換為原始型別 toPrimitive([]);

[].valueOf();//[],不是原始型別

[].toString();//"",真是令人髮指的轉換

2. 都為string,所以返回字串想家的結果

return "" + "";複製程式碼
複製程式碼

{} + [] 與 [] + {}

複製程式碼
{} + []
1. 在瀏覽器中,JS引擎認為第一個{}為空程式碼塊,所以 這裡的 '+'是單目運算子(node中認為是物件,解析為"[object Object]")

ToPrimitive([]); //""

2. Number("");//0


[] + {}
1. ToPrimitive([]); //""
    ToPrimitive({}); //"[object Object]"

2. 都為string
    return "" + "[object Object]";//"[object Object]"複製程式碼
複製程式碼
複製程式碼

PS: [].valueOf 為[], 但在ES6中JS會優先呼叫[Symbol ToPrimitive]來轉換為原始型別。

比較運算

首先,比較運算分為2種, 一種為嚴格比較(===),只有型別相等,值也一致時才會為true,否則為false, 另一種為抽象相等也叫寬鬆相等(==),現將運算數轉化為相同型別,再做比較,具體過程見 Abstract Equality Comparison Algorithm

這個演算法大致說了這麼幾個情況,x+y

  • xy都為Null或undefined,return true;
  • x或y為NaN, return false;
  • 如果x和y為String,Number,Boolean並且型別不一致,都轉為Number再進行比較
  • 如果存在Object,轉換為原始值,比較

回到這篇文章的導火索,[] == false

複製程式碼
1.存在object, 轉化為原始值
ToPrimitive([]); // ''

2.一個string, 另外為boolean,都轉為number
Number('');//0
Number(false);//0

3.return 0 == 0;/true複製程式碼
複製程式碼

備註

ToPrimitive

valuetoNumber toString toBoolean
NaN NaN "NaN" false
Infinity Infinity "Infinity" true
[] 0 '"" true
[1] 1 "1" true
null0"null"false
undefinedNaN"undefined"false
{}NaN"[object Object]"true
function()NaN"function"true

ToNumber

ToString


相關文章