1. 程式人生 > >為什麼 ["1", "2", "3"].map(parseInt) 返回 [1,NaN,NaN] ---續

為什麼 ["1", "2", "3"].map(parseInt) 返回 [1,NaN,NaN] ---續

最近碰到了[‘1’,’2’,’3’].map(parseInt)這種看似不起眼陷阱卻極大的問題。

這乍一看,感覺應該會輸出[1,2,3]。但是,實際上並不是我們想的這樣。你可以現在開啟console,看看輸出的結果。

出乎意料結果竟然是[1,NaN,NaN].

至於為什麼是這樣,下面一步一步的解釋。

parseInt()

parseInt是JS的一個內建函式,它可以將字元創解析成一個數值表示式並將該數值返回。呼叫的時候如下:

var n = parseInt('123')
  • 1

函式返回的結果是123 ,並賦值給變數n。這裡應該沒什麼問題。

當然,如果傳入pasreInt()引數並不能被解析成數值表示式,那麼函式執行後將返回 NaN。NaN

的含義就是Not a Number(不是一個數值)。

var m = parseInt('xyz)
  • 1

此時m的值應該就是NaN。

Array.prototype.map

map 是ECMAScript 5 的一個內建數值方法.map把函式作為它的引數。map遍歷陣列中所有的元素,並且為每個元素呼叫一次這個傳入map中的函式,當前元素作為引數傳入該函式。map函式呼叫完畢之後將返回的結果存入一個新陣列中。看下面這個例子:

[1,2,3].map(function(value){
        return value+1
    })
  • 1
  • 2
  • 3

很好理解,最後的結果的應該是[2,3,4].這種呼叫應該是很常見的。但是有時將已經存在的函式作為引數傳入map是無效的啊,比如parseInt.

清楚了parsenInt和map的基本用法之後,再來看看比較容易忽視的問題。

解決方法

現在來看看ECMAScript 5 中parseInt 的用法。有會發現它可以接受兩個引數。第一個引數是要被解析的字串,第二個引數是要解析成數值的基礎。所以, parseInt('ffff',16) 返回的結果是65535,然而

parseInt('ffff',8)
  • 1

返回的結果是NaN,因為“ffff”並不能解析成一個八進位制的數值。如果第二個引數省略了或者是0,parseInt預設為是10進位制 所以

parseInt("12",10) ;
parseInt("12") ;
parseInt("12",0
) ;
  • 1
  • 2
  • 3

都會返回數值12。

現在看看map 的定義。map指定傳入的函式引數作為callbackfn.規範裡指出:“callbackfn呼叫時需要傳入三個引數:元素的值,元素的索引和正在被遍歷的物件”。仔細體會一下,原本我們以為對parseInt的三次呼叫是這樣:

parseInt('1')
parseInt('2')
parseInt('3')
  • 1
  • 2
  • 3

然而實際上是這樣呼叫的:

parseInt('1',0,theArray);
parseInt('2',1,theArray);
parseInt('3',2,theArray);
  • 1
  • 2
  • 3

這裡theArray 就是原始陣列[‘1’,’2’,’3’]。

JS函式一般會自動忽視多餘的引數,parseInt僅僅需要兩個引數,所以我們並不需要擔心這個theArray引數對parseInt的影響。

重點來了,第二個引數是如何影響parseInt的

在第一次呼叫時,parseInt的第二個引數是0,上面已經說了,所以第一呼叫 parseInt('1',0) 返回1. 第二次呼叫第二個引數是1,也是說1作為數值的基礎。規範裡說的很清楚了,如果基礎是非0或者小於2,函式都不會查詢字串直接返回NaN。 第三次呼叫時,2作為基數。這就意味著字串將被解析成位元組數,也就是僅僅包含數值0和1。parseInt的規範第十一步指出,它僅嘗試分析第一個字元的左側,這個字元還不是要求基數的有效數字。這個字串的第一個字元是“3”,它並不是基礎基數2的一個有效數字。所以這個子字串將被解析為空。第十二步說了:如果子字串被解析成空了,函式將返回為NaN。

所以這裡的結果就應該是[1,NaN,NaN].

這裡問題所在就是容易忽視parseInt是需要兩個引數的。map中有三個引數。所以這裡結合起來,就導致了上面問題。 換個方式寫:

['1','2','3'].map(function(value){
        return parseInt(value)
})
  • 1
  • 2
  • 3

這樣就不會出錯。

當然,我們也可以寫:

['1','2','3'].map(Number)
  • 1