當前位置:
首頁 > 知識 > 一道面試題引發的對JavaScript類型轉換的思考

一道面試題引發的對JavaScript類型轉換的思考

來自:ChokCoco


鏈接:www.cnblogs.com/coco1s/p/6509141.html(點擊尾部閱讀原文前往)


已獲轉載授權

最近群里有人發了下面這題:


實現一個函數,運算結果可以滿足如下預期結果:


對於一個好奇的切圖仔來說,忍不住動手嘗試了一下,看到題目首先想到的是會用到高階函數以及 Array.prototype.reduce()。


高階函數(Higher-order function):高階函數的意思是它接收另一個函數作為參數。在 javascript 中,函數是一等公民,允許函數作為參數或者返回值傳遞。

得到了下面這個解法:


驗證了一下,發現錯了:


上面的解法,只有在 add()() 情形下是正確的。而當鏈式操作的參數多於兩個或者少於兩個的時候,無法返回結果。


而這個也是這題的一個難點所在,add()的時候,如何既返回一個值又返回一個函數以供後續繼續調用?


後來經過高人指點,通過重寫函數的 valueOf 方法或者 toString 方法,可以得到其中一種解法:

嗯?第一眼看到這個解法的時候,我是懵逼的。因為我感覺 fn.valueOf() 從頭到尾都沒有被調用過,但是驗證了下結果:


神奇的對了!那麼玄機必然是在上面的 fn.valueOf = function() {} 內了。為何會是這樣呢?這個方法是在函數的什麼時刻執行的?且聽我一步一步道來。


valueOf 和 toString


先來簡單了解下這兩個方法:


Object.prototype.valueOf()

用 MDN 的話來說,valueOf() 方法返回指定對象的原始值。


JavaScript 調用 valueOf() 方法用來把對象轉換成原始類型的值(數值、字元串和布爾值)。但是我們很少需要自己調用此函數,valueOf 方法一般都會被 JavaScript 自動調用。


記住上面這句話,下面我們會細說所謂的自動調用是什麼意思。


Object.prototype.toString()

toString() 方法返回一個表示該對象的字元串。


每個對象都有一個 toString() 方法,當對象被表示為文本值時或者當以期望字元串的方式引用對象時,該方法被自動調用。


這裡先記住,valueOf() 和 toString() 在特定的場合下會自行調用。


原始類型


好,鋪墊一下,先了解下 javascript 的幾種原始類型,除去 Object 和 Symbol,有如下幾種原始類型:


Number


String


Boolean


Undefined


Null


在 JavaScript 進行對比或者各種運算的時候會把對象轉換成這些類型,從而進行後續的操作,下面逐一說明:


String 類型轉換


在某個操作或者運算需要字元串而該對象又不是字元串的時候,會觸發該對象的 String 轉換,會將非字元串的類型嘗試自動轉為 String 類型。系統內部會自動調用 toString 函數。舉個例子:


轉換規則:


如果 toString 方法存在並且返回原始類型,返回 toString 的結果。


如果 toString 方法不存在或者返回的不是原始類型,調用 valueOf 方法,如果 valueOf 方法存在,並且返回原始類型數據,返回 valueOf 的結果。


其他情況,拋出錯誤。


上面的例子實際上是:


其中,obj.toString() 的值為 "[object Object]"。


假設是數組:


上面 + arr ,由於這裡是個字元串加操作,後面的 arr 需要轉化為一個字元串類型,所以其實是調用了 + arr.toString() 。


但是,我們可以自己改寫對象的 toString,valueOf 方法:


plain


上面 alert(obj + 1 ) ,obj 會自動調用自己的 obj.toString() 方法轉化為原始類型,如果我們不重寫它的 toString 方法,將輸出 [object Object]1 ,這裡我們重寫了 toString ,而且返回了一個原始類型字元串 111 ,所以最終 alert 出了 1111。


上面的轉化規則寫了,toString 方法需要存在並且返回原始類型,那麼如果返回的不是一個原始類型,則會去繼續尋找對象的 valueOf 方法:


下面我們嘗試證明如果在一個對象嘗試轉換為字元串的過程中,如果 toString() 方法不可用的時候,會發生什麼。


這個時候系統會再去調用 valueOf() 方法,下面我們改寫對象的 toString 和 valueOf:


從結果可以看到,當 toString 不可用的時候,系統會再嘗試 valueOf 方法,如果 valueOf 方法存在,並且返回原始類型(String、Number、Boolean)數據,返回valueOf的結果。


那麼如果,toString 和 valueOf 返回的都不是原始類型呢?看下面這個例子:


可以發現,如果 toString 和 valueOf 方法均不可用的情況下,系統會直接返回一個錯誤。


添加於 2017-03-07:在查證了 ECMAScript5 官方文檔後,發現上面的描述有一點問題,Object 類型轉換為 String 類型的轉換規則遠比上面複雜。轉換規則為:1.設原始值為調用 ToPrimitive 的結果;2.返回 ToString(原始值) 。關於 ToPrimitive 和 ToString 的規則可以看看官方文檔:ECMAScript5 -- ToString


Number 類型轉換


上面描述的是 String 類型的轉換,很多時候也會發生 Number 類型的轉換:


調用 Number() 函數,強制進行 Number 類型轉換


調用 Math.sqrt() 這類參數需要 Number 類型的方法


obj == 1 ,進行對比的時候


obj + 1 , 進行運算的時候


與 String 類型轉換相似,但是 Number 類型剛好反過來,先查詢自身的 valueOf 方法,再查詢自己 toString 方法:


如果 valueOf 存在,且返回原始類型數據,返回 valueOf 的結果。


如果 toString 存在,且返回原始類型數據,返回 toString 的結果。


其他情況,拋出錯誤。


按照上述步驟,分別嘗試一下:


Boolean 轉換


什麼時候會進行布爾轉換呢:


布爾比較時


if(obj) , while(obj) 等判斷時


簡單來說,除了下述 6 個值轉換結果為 false,其他全部為 true:


undefined


null


-0


0或+0


NaN


(空字元串)


Function 轉換


好,最後回到我們一開始的題目,來講講函數的轉換。


我們定義一個函數如下:


如果我們僅僅是調用 test 而不是 test() ,看看會發生什麼?

一道面試題引發的對JavaScript類型轉換的思考



可以看到,這裡把我們定義的 test 函數的重新列印了一遍,其實,這裡自行調用了函數的 valueOf 方法:

一道面試題引發的對JavaScript類型轉換的思考



我們改寫一下 test 函數的 valueOf 方法。


與 Number 轉換類似,如果函數的 valueOf 方法返回的不是一個原始類型,會繼續找到它的 toString 方法:


破題


再看回我正文開頭那題的答案,正是運用了函數會自行調用 valueOf 方法這個技巧,並改寫了該方法。我們稍作改變,變形如下:


當調用一次 add 的時候,實際是是返回 fn 這個 function,實際是也就是返回 fn.valueOf();


其實也就是相當於:


當鏈式調用兩次的時候:


當鏈式調用三次的時候:


可以看到,這裡其實有一種循環。只有最後一次調用才真正調用到 valueOf,而之前的操作都是合并參數,遞歸調用本身,由於最後一次調用返回的是一個 fn 函數,所以最終調用了函數的 fn.valueOf,並且利用了 reduce 方法對所有參數求和。


除了改寫 valueOf 方法,也可以改寫 toString 方法,所以,如果你喜歡,下面這樣也可以:


這裡有個規律,如果只改寫 valueOf() 或是 toString() 其中一個,會優先調用被改寫了的方法,而如果兩個同時改寫,則會像 Number 類型轉換規則一樣,優先查詢 valueOf() 方法,在 valueOf() 方法返回的是非原始類型的情況下再查詢 toString() 方法。


後記


像阮一峰老師所說的,「炫耀從來不是我寫作的動機,好奇才是」。本文行文過程也是我自己學習的一個過程,過程中我也遇到了很多困惑,所以即便查閱了官方文檔及大量的文章,但是錯誤及疏漏仍然在所難免,歡迎指正及給出更好的方法。


對於類型轉換,最好還是看看 ECMAScript 規範,拒絕成為伸手黨,自己多嘗試。另外評論處有很多人提出了自己的疑問,值得一看。


本文編號2272,以後想閱讀這篇文章直接輸入2272即可。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 程序猿 的精彩文章:

Web前端攻防,一不小心就中招了
Vue開源項目庫匯總
猿哥原創精華文章總結
遊戲開發完整學習路線

TAG:程序猿 |

您可能感興趣

Javascript中的類型轉換
JavaScript 類型轉換
JavaScript 數據類型
淺談Greenplum的Boolean類型與Text類型之間的轉換
Kotlin跟findViewById 的類型推導衝突問題
Spring Data JPA與PostgreSQL的jsonb類型集成與支持
Swift 可選(Optionals)類型
Javascript 判斷變數類型的陷阱 與 正確的處理方式
JavaScript深入淺出補充——(一)數據類型,表達式和運算符
從 JavaScript到TypeScript-聲明類型
想看點不一樣類型的韓劇?真心推薦tvN《Dear My Friends》!
編譯期類型檢查 in ClojureScript
自定義一個 Writable 類型 TextPair
細說Nullable類型
Java多態對象的 類型轉換
Java多態對象的類型轉換
C 可空類型(Nullable)
Authenticode簽名偽造——針對文件類型的簽名偽造
val b=a?:0,a是Double 類型,那 b 是什麼類型?