點燈坊

失くすものさえない今が強くなるチャンスよ

為什麼 ['1', '7', '11'].map(parseInt) 為 [1, NaN, 3] ?

Sam Xiao's Avatar 2020-01-30

這是 ECMAScript 的老梗,但為什麼 Ramda 的 map() 就沒事呢 ?

Version

macOS Catalina 10.15.2
VS Code 1.40.2
Quokka 1.0.259
ECMAScript 2015
Ramda 0.26.1

map()

import { map } from 'ramda'

let data = ['1', '7', '11']

data.map(parseInt) // ?
map(parseInt)(data) // ?

同樣是將 parseInt() 傳進 map(),但 ECMAScript 的 map() 與 Ramda 的 map() 竟然結果不同 ?

parseint000

ECMAScript

Array.prototype.map()
arr.map(function callback( currentValue[, index[, array]]) { // return element for new_array }[, thisArg])
將 array 轉換成新 array

ECMAScript 的 callback 會回傳 3 個 argument:

  • currentValue:目前 element 值
  • index:目前 index 值
  • array:完整 array

因此 map(parseInt) 依次會如下執行:

parseInt('1', 0, ['1', '7', '11']) // ?
parseInt('7', 1, ['1', '7', '11']) // ?
parseInt('11', 2, ['1', '7', '11']) // ?

已經可以發現 parseInt() 結果已經不如預期。

parseint001

parseInt(string [, radix])
將 string 轉換成 int

其中第二個 argument 為 radix:

  • 0:自動改為 10
  • 1:回傳 NaN
  • 2 ~ 36:radix 合理值
  • 37 以上:回傳 NaN

因此 parseInt('1', 0)10parseInt('7', 1)NaN,而 parseInt('11', 2)3

至於第三個 argument 會自動忽略。

Ramda

map()
Functor f => (a → b) → f a → f b
將 array 轉換成新 array

重點在於 map() 的 callback 只有 (a -> b),因此 parseInt() 的第二個 argument 為 undefined,所以 radix 為 10,結果如預期沒有任何意外。

parseInt('1') // ?
parseInt('7') // ?
parseInt('11') // ?

parseInt() 如預期結果。

parseint002

Conclusion

  • 由於 ECMAScript map() 的 callback 有 3 個 argument,都傳給 parseInt() 造成不可預期結果;但 Ramda 的 callback 只有 1 個 argument,因此如預期 radix 為 10 轉成 int

Reference

Eric Tong, Whe [‘1’, ‘7’, ‘11’].map(parseInt) return [1, NaN, 3] in JavaScript