問題來源
這個問題的來源是學習廖雪峰老師JS教程。問題如下:小明希望利用map()
把字符串變成整數,他寫的代碼很簡潔:
'use strict';
var arr = ['1', '2', '3'];
var r;
r = arr.map(parseInt);
console.log(r);
// [1, NaN, NaN]
為什么不是[1, 2, 3]?這是因為兩個兩個函數的定義有沖突。下面詳解:
map的定義
注意到這個問題的原因是參考了這個國外某博客JavaScript可選參數危險。首先,我們了解一下map方法的定義,如下:
var new_array = arr.map(function callback(currentValue[, index[, array]]) {
// Return element for new_array
}[, thisArg])
其中參數有一個函數callback
,這個函數需要三個參數:currentValue
(必須):處理的數組中的當前元素;index
(可選):當前處理的元素在數組中的索引值;array
(可選):調用map的數組。thisArg
(可選):對象作為該執行回調時使用,傳遞給函數,用作 "this" 的值。
如果省略了 thisValue ,"this" 的值為 "undefined"。
所以,map函數接收的是兩個參數,一個函數,另一個是thisArg
。這里我們主要關注的是其中的函數接收三個參數,一個必須,兩個可選。問題就是出現在這里,函數接收三個參數。
parseInt的定義
這個函數用於解析一個字符串,並返回一個整數。定義如下:
parseInt(string, radix)
參數string
(必須):表示的是要被解析的字符串。radix
(可選):表示表示要解析的數字的基數。該值介於 2 ~ 36 之間。如果省略該參數或其值為 0,則數字將以 10 為基礎來解析。如果它以 “0x” 或 “0X” 開頭,將以 16 為基數。如果該參數小於 2 或者大於 36,則 parseInt() 將返回 NaN。第二個可選參數,就是問題的另一來源了。
問題詳解
了解完兩個函數的定義后,我們就知道小明代碼的問題了:
var arr = ['1', '2', '3'];
var r;
r = arr.map(parseInt);
我們通常以為上述代碼中 arr.map(parseInt);
表示的是
parseInt("1")
parseInt("2")
parseInt("3")
實際上應該是:
parseInt("1", 0, arr)
parseInt("2", 1, arr)
parseInt("3", 2, arr)
其中的arr
是['1', '2', '3']
。我們知道,JavaScript函數通常會忽略額外的參數,並且parseInt
只需要兩個參數,因此我們不必擔心theArray
這些調用中參數的影響。但是,第二個參數對parseInt
影響很大。parseInt("1", 0)
沒有問題,0表示的是以二進制為基數解析‘1’
。但是后面的parseInt("2", 1)
以及parseInt("3", 2)
就有問題了。1
在parseInt
中是無效的基數,返回‘NaN’
值;2
雖然是有意義的基數,但是因為‘3’
不是合法的二進制數,所以也返回的是NaN
。至此,問題錯誤已經明了。下面是解決方案。
解決方案
解決的方法很簡單,就是修飾一些parseInt
函數。代碼如下:
'use strict';
var arr = ['1', '2', '3'];
var r;
r = arr.map(function parseInt2(x) {
return parseInt(x);
});
console.log(r);
或者我們使用Number()
方法,這樣也會把字符串參數解析成十進制數,並且只需要一個參數。代碼如下:
'use strict';
var arr = ['1', '2', '3'];
var r;
r = arr.map(Number);
console.log(r);
當然,也可以從map
下手解決問題。一個可能的方法如下:
Function.prototype.only = function(numberOfArgs) {
var self = this; //the original function
return function() {
return self.apply(this,[].slice.call(arguments,0,numberOfArgs))
}
};
arr.map(parseInt.only(1));
以上,就是JavaScript中map與parseInt沖突的問題的來源、解析、解決了。