【WEB前端】百度前端面試經歷小研究1——JavaScript 型別轉換

於明昊發表於2013-08-17

近日看到一篇叫做百度web前端面試經歷(原文已佚,這篇也是轉載)的文章,其中作者詳細的描述了自己去百度web前端面試的經歷,Interviewer問及問題的範圍也十分廣泛,有一定的參考價值。不過這篇文章的美中不足之處就在於,作者沒有對問題給出一個合理的解釋,有些面試的問題作者本身所知也並不詳細。這裡就寫一篇小文章分析一下面試中提到的問題。

面試官:javascript的型別轉換(比如"2"*1, "a"*1)。

我:javascript會呼叫valueOf來轉換為一個基本資料型別,在這種情況下,如果javascript不能通過valueOf轉成一個number,會嘗試呼叫toString,然後再轉。實在無法轉就只能NaN了。

由於JS一切皆物件的基礎理念,Object 在 JS中廣泛存在,但JS的所有變數還是分為原始值和物件。JS的型別轉換不僅僅是狹義意義上的原始值(即 string、int 和 boolean)之間的互相轉換,按照犀牛書中所說,主要分為三個大類:

  • 原始值轉原始值
  • 原始值轉物件
  • 物件轉原始值

在此基礎上,也有根據轉換方式將其分為“顯式轉換”和“隱式轉換”的說法,所謂的顯示轉換,即運用基本型別函式的建構函式(Number()String()Boolean()Object())對某個變數進行人為的強制型別轉換;所謂的隱式轉換,是指在進行運算時JavaScript進行自動的型別轉換,如 == 運算子、+ 運算子等。其中有幾個比較著名的隱式轉換,可以作為型別轉換的慣用方式:

x+''   //等價於 String(x)
+x     //等價於 Number(x)
!!x    //等價於 Boolean(x)

然後我們再來看一下,型別轉換的三個大類。

  • 原始值轉原始值

    相對簡單,可以採用上面所述的顯式或隱式的轉換方式,在字串、數字和布林值之間進行轉換。

    但是這裡有一些值得注意的轉換,例如空字串、正負0、NaN 在轉化為布林值時均為false(注意字串 'false' 轉化為布林值為真!),在一些情況下可以進行快速的判斷;字串如果不能轉為數字,則轉化為 NaNundefined 也會轉化為這個),而 null 卻可以轉化為數字0;數字裡的 Infinity-Infinity 都可以轉化為對應的字串,轉化為布林值時也是true。

  • 原始值轉物件

    這裡實際上就是使用基本函式的建構函式,加上 new 運算子,生成一個物件。這裡又幾個巧妙的用法,比如數值可以用 toString() 方法進行快速的進位制轉換;在我前幾日寫過的一篇小文:JavaScript絕句小研究中,絕句6中也展示了一個用 toFixed() 函式進行位數擷取的功能。

  • 物件轉原始值

    對於物件轉化為布林值而言,比較簡單:均為true(這一點和python又有不同,比如空陣列和空物件,在JS中也視為true)。對於轉化為數值和字串:根據犀牛書中所說,所有的物件都繼承了兩個方法來進行到原始值的轉換,一個是 toString ,一個是 valueOf,這兩個方法顧名思義,一個是轉化為字串的,一個是轉化為數值的。只是 toString 的方法強大一些,基本上什麼都能轉;valueOf 相比之下弱一些,唯一能完整轉換的物件就是把 Date() 物件轉化為對應的時間戳(絕句的第二個)。

    那麼作者在面試中說的正確與否呢?犀牛書裡說,這裡分為三個部分,

    • 隱式轉換:除Date外,統統是先 valueOf、再 toString(Date 在 +== 時優先轉化為字串)。
    • 顯式物件轉數值:物件轉數值的方法,先嚐試 valueOf (如果能返回原始值就退出)、如果不行再嘗試 toString ,否則NaN。
    • 顯式物件轉字串:物件轉字串的過程恰好是反過來的,先嚐試 toString (能返回原始值就退出),再 valueOf ,否則進行報錯。

    我們可以實際寫一個小例子測試一下,這裡我們用陣列來表示一個物件,觀察其型別轉換:

    Array.prototype.valueOf = function() {return 10}     // 重寫valueOf函式
    Array.prototype.toString = function() {return '88'}  // 重寫toString函式
    !![]        // 結果為true,任意物件轉換為布林值均為真
    [] + 1      // 結果為11,在隱式轉換裡,先valueOf,有原始值且能參與運算則直接返回
    [] + ''     // 結果為'10',隱式轉換,雖然是要轉換為字串,但同樣是先進行了valueOf
    Number([])  // 結果為10,顯示轉換,先進行valueOf
    String([])  // 結果為'88',顯式轉換,先進行toString
    Array.prototype.valueOf = null                       // 令valueOf失效
    [] + 1      // 結果為'881',隱式轉換valueOf不行後轉到toString,得到原始值字串直接返回
    Number([])  // 同理,結果為88
    

所以實際上作者所說比較片面,實際上侷限與隱式轉換的情況——不過看面試官的提問,大概也就是要說道這點為止吧。經過上面的分析,其實有很多看起來頭大的題目都可以迎刃而解,比如這道為什麼 ++[[]][+[]]+[+[]] = 10?;但是在瀏覽 justjavac 的部落格時還是發現了一個跟型別轉換相關,但又有點蹊蹺的題目:JavaScript中,{}+{}等於多少?,有興趣的讀者可以研究一下。

相關文章