【js小知識】[]+ {} =?/{} +[] =?(關於加號的隱式類型轉換)

寫在前面

今天來聊一聊JS中的加號,就是這個小小的‘+’,你可能會覺得加法不久兩種狀況,數字和字符串啊。我最開始也是這麼認爲的,後來真香…(小加號解決了我最近項目的一個小問題放在文末來講)javascript

每每越是細小的知識點越容易被忽視。越細小的知識越能考驗基礎比。如{}+{}的結果是什麼?[]+{}的結果是什麼?html

若是你還知道那就花十分鐘康康這篇文章吧。java

js的數據類型

在js中有兩類數據類型:web

  • 原始類型:String、Number、Null、Boolean、Undefined、Symbol、Bigint
  • 引用類型:Object(包括數組array和函數function)

特別提醒:MDN文檔中js的數據類型有8中,7中數據類型和對象類型,Bigint能夠用任意精度表示整數,這篇文章不會使用數組

八種數據類型傳送門:MDN文檔markdown

加法的一元運算

加法一元運算的內部操做

  • 語法格式: + Expression
  • 說明: 加法的一元運算中,Expression會進行Number(Expression)操做。
    Number是一個將參數轉換成數字的方法。

不一樣類型參數返回的結果

實踐檢驗

在控制檯中輸入如下一元運算符,能夠看到他們的結果app

結果分析

  • +ace運算分析:

因爲ace是一個沒有定義的變量,那麼應該是undefined類型,因此Tonumber返回的結果是NaN。函數

  • +''運算分析 :

這裏加號後面是一個空字符串,空字符串轉數字就是0,結果返回0。ui

  • +false的運算分析:

false是一個布爾值,轉換成數字就是0。url

  • +'123'和+'qqq'的運算分析:

這兩個加號後都是字符串,前者是純數字,後者是字符,因此前面可以轉換成數字,後面的非樹轉換成NaN。

  • +{}和+[]的分析:

這裏加號後面是對象類型,對象類型會先轉換成原始值(ToPrimitice方法),而後將這個原始值再轉換成數字類型。關於ToPrimitice方法將在後面詳細介紹。

ToPrimitive(input,PreferredType?)方法介紹

ToPrimitice是JavaScript引擎內部的一個操做,將參數input轉換成原始值。內部執行原理有四部

  1. 若是這個參數自己就是原始值,那麼返回自己。

  2. 若是不是參數調用ValueOf方法,valueOf方法返回的結果若是是原始值就返回該原始值。

  3. 若是不是就調用toString方法,toString方法的結果若是是原始值就返回該值。

  4. 若是結果還不是原始值,拋出 TypeError 異常。

tips1

若是valueOf和toString方法不瞭解,參考NMD官方文檔,這裏給出連接。valueOf方法官方文檔,另外給出toString方法實踐的截圖

tips2

參數 PreferredType 能夠是 Number 或者 String。若是ToPrimitive(input,PreferredType?)中PreferredType 被標誌爲 String,則轉換操做的第二步和第三步的順序會調換。 若是沒有 PreferredType 這個參數,則 PreferredType 的值會按照Number類型上面的四步處理。


是否還記得前面一元運算的實踐小案例呢?

+{}會進過ToPrimitive方法轉換成原始值後再進行toNumber。最後的結果是NaN,一塊兒來看看這個轉換的過程吧

//先ToPrimitive({})再Number()
+{}
Number({})
Number({}.valueOf());//結果是{}非原始值 {}.valueOf() isn’t primitive
Number({}.toString())  //結果是字符串,爲原始值
Number("[object Object]")//"[object Object]"非數字的字符串
NaN
複製代碼

同理,+[]也是同樣大的道理

Number([])
Number([].toString())//[].valueOf() isn’t primitive
Number('')
0
複製代碼

加法的二元運算

內部原理

有下面這樣的一個加法操做。

value1 + value2

在計算這個表達式時,內部的操做步驟是這樣的

將兩個操做數轉換爲原始值 (如下是數學表示法的僞代碼,不是能夠運行的 JavaScript 代碼):
prim1 := ToPrimitive(value1) prim2 := ToPrimitive(value2)
PreferredType 被省略,所以 Date 類型的值採用 String,其餘類型的值採用 Number。

若是 prim1 或者 prim2 中的任意一個爲字符串,則將另一個也轉換成字符串,而後返回兩個字符串鏈接操做後的結果。

不然,將 prim1 和 prim2 都轉換爲數字類型,返回他們的和。

實踐檢驗

咱們來看看文章開頭提到的例子,{}+{}、[]+[]這些騷操做。

{}+{}的結果

對於{}+{},加號的兩邊都是對象類型。先使用ToPrimitive將他們轉換成字符串類型。回顧一下ToPrimitive方法內部轉換四個過程,應該調用toString將他們轉換爲兩個相同的字符串。相加的結果仍是一個字符串。

咱們來四步分析看看吧

//ToPrimitive({})
1. {}是對象類型非原始值
2. {}.valueOf()f方法返回的結果仍是非原始值
3. {}.toString()方法返回結果是"[object Object]"字符串
4. 是一個原始值
複製代碼

因此上面的{}+{}的結果是兩個"[object Object]"字符串相加,拼接爲的字符串"[Object Object][object Object]"就是最後的結果。

{}+[]的結果

這個{}+[]和上滿同樣的道理,只不過[].toString返回的結果是是一個空字符,這裏爲了方便你們,截圖展現一下。


可是對於toString和valueOf()方法必定要熟悉,不知道就去查。

因此{}+[]的結果就是"[object Object]"字符串。

可能有的人會對上面的分析表示懷疑,我在終端中輸入這兩個二元運算,打印的結果和咱們分析的同樣,附上結果圖:

一個有趣的現象

細心的朋友看到上面的截圖可能會有一個疑問。
同樣嗎?不同啊,{}+[]的結果是0啊,咱們上面的分析不該該是一個字符串嗎?這是爲何,別急,下面將爲你慢慢道來。

緣由:{}+[]是把{}解析爲爲一個空的代碼塊而且忽略了它,可能有人問什麼是代碼塊。好比你寫的一個箭頭函數()=>{},這裏面的大括號{}內的內容就是代碼塊。

在{}+[]中忽略了空的代碼塊,就變成了了一元運算+[]。因此結果是0。當咱們讓數組放在前面,對象放在後面的時候[]+{}就是咱們前面分析的結果。總之不能讓{}出如今開頭。

拓展

這裏再拓展一下,{}.toString()方法報錯


這和前面是一個道理,大括號開頭的js引擎會認爲是代碼塊,因此會報錯。改爲({}).toString()就行了。

文章末尾

參考文章

文章的靈感

這篇文章的靈感來源最近在作一個小項目,項目裏有一個數據分頁功能。分頁功能中上一頁就是當前頁減去1,那麼下一頁就是當前頁加1咯。可是最後報錯了。

報錯的緣由:當前的頁碼是從url地址欄中獲取的,獲取的當前頁page是字符串類型。字符串的減法存在隱式類型轉換(轉換成數字);字符串加上一個數字的結果仍是字符串不是數字

解決錯誤:轉換思路,讓當前頁-0隱式轉換成數字後再加1就能夠啦。

經過這個小事兒我就是想說一些很細小的東西每每是你們容易忽略的,每一個知識也都是有他的意義的。

看完的收穫

但願看完這篇文章的你收穫瞭如下知識:

  • valueOf()和toString()方法的運用
  • ToPrimitive(input,PreferredType?)方法的原理
  • 任意數據類型的一元加法運算
  • 任意數據類型的二元加法運算

其中最核心最重要的就是ToPrimitive(input,PreferredType?)方法的原理,再次回顧一下這個原理:這個方法是將某個類型的數據轉換成原始類型,他的返回值必定是原始值,若是參數input自己就是原始值就返回這個原始值;

若是不是就調用valueOf方法或者toString方法,至於先調用哪個看PreferredType是number仍是string,默認狀況下是number,即先進行valueOf方法看結果是不是原始值,若是不是再使用tosting方法看返回的結果是不是原始值;若是還不是就會報錯。

最後,走心文章,但願可以用心看完,若有錯誤地方歡迎評論區指正


下篇文章見…
相關文章
相關標籤/搜索