快速理解["1", "2", "3"].map(parseInt)

1,877 阅读5分钟

这道很经典的面试题已经过去很多年了,但是现在依然被很多人 传颂 着。以前研究过这道题,觉得弄懂了。但是今天被别人问起这道题时回答的又有点 模棱两可

我们先看一下输出结果

考察点:

  1. Array.map()
  2. parseInt()
  3. 进制(进制之间转换)

那我们分别研究一下这三个要点。

Array.map()

这个应该是我们最常用的Array方法之一。早在ES6就有了。
map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。

var new_array = arr.map(function callback(currentValue,index,array) {
 // Return element for new_array 
},thisArg)
  • Array.map()参数

callback 生成新数组元素执行的函数(三个参数)
    currentValue callback 数组中正在处理的当前元素。
    index callback 数组中正在处理的当前元素的索引。
    array map 方法被调用的数组。

thisArg 执行 callback 函数时使用的this 值。

返回值 一个新数组,每个元素都是回调函数的结果。

  • Array.map()示例

let arr = ["1","2","3"].map(function callback(currentValue,index,array) {
    console.log(`${currentValue}+${index} [${array}]`)
    return currentValue*2
});
console.log(arr);

output:
// 1+0 [1,2,3]
// 2+1 [1,2,3]
// 3+2 [1,2,3]
// [2, 4, 6]


var numbers = [1, 4, 9];
var roots = numbers.map(Math.sqrt);
// roots的值为[1, 2, 3], numbers的值仍为[1, 4, 9]

parseInt()

parseInt(string, radix);
  • parseInt()参数

string 要被解析的值。如果参数不是一个字符串,则将其转换为字符串。
radix 一个介于2和36之间的整数(数学系统的基础),表示上述字符串的基数。当未指定基数时,不同的实现会产生不同的结果,通常认为其值默认为10,但是请在使用时总是显示的指定它。

注意 radix参数为n 将会把第一个参数看作是一个数的n进制表示,而返回的值则是十进制的。
这是MDN上的解释 parseInt(string, radix) 将一个字符串 string 转换为 radix 进制的整数, radix 为介于2-36之间的数。
对于上面两句话我理解的时候总是有点怪。我觉得可以这样理解parseInt(string, radix), 把radix进制的string转成10进制的值,并返回
例如:

parseInt('123', 5) // 将'123'看作5进制数,返回十进制数38 => 1*5^2 + 2*5^1 + 3*5^0 = 38

这里将5进制的123转成10进制为38

  • parseInt()示例

parseInt("20"); //返回20  radix默认为10,也就是10进制的20转成10进制,那么也是20
parseInt("10",2);//返回2  radix为2,也就是2进制的10转成10进制,1*2^1+1*2^0 = 2
parseInt("103",2);//返回2 radix为2,也就是2进制的103转成10进制,因为2进制只包括0,1,所以3不是2进制的数字(只计算3之前的值也就是10),1*2^1+1*2^0 = 2
parseInt("213",4);//返回39 radix为4,也就是4进制的213转成10进制,2*4^2+1*4^1+3*4^0 = 32+4+3 = 39
...

这里进制是怎样转换的呢,比如4进制的213转成10进制

2*4^2+1*4^1+3*4^0 = 32+4+3 = 39

拿第一个数*进制的第一个数的位数的次方+第二个数*进制的第二个数的位数的次方+···直到加上最后一位数*进制的0次方(位数从右向左数0,1,2...)

看下面的介绍:

在基数radix为 undefined,或者基数为 0 或者没有指定的情况下,JavaScript 作如下处理:

如果字符串 string 以"0x"或者"0X"开头, 则基数是16 (16进制).
如果字符串 string 以"0"开头, 基数是8(八进制)或者10(十进制),那么具体是哪个基数由实现环境决定。ECMAScript 5 规定使用10,但是并不是所有的浏览器都遵循这个规定。因此,永远都要明确给出radix参数的值。
如果字符串 string 以其它任何值开头,则基数是10 (十进制)。
如果第一个字符不能被转换成数字,parseInt返回NaN。

算术上, NaN 不是任何一个进制下的数。 你可以调用isNaN 来判断 parseInt 是否返回 NaN。NaN 参与的数学运算其结果总是 NaN。

那我们来看这几个例子怎么返回

parseInt("1",0); //radix为10也就是10进制的1转10进制,也就是 1
parseInt("2",1);//radix 介于2和36之间的整数,当radix不在这个范围内的解析全都返回 NaN
parseInt("3",2);// 3不是2进制的数字,3前面也没有其他数字,所以这里返回 NaN

我们知道parseInt为一个函数,其源代码应该是这个样子的:

function parseInt(string, radix){
    return xxx;
}

当写成这样Arrar.map(parseInt)的形式时 callback === parseInt
parseInt(string, radix)的两个参数分别对应currentValue index
这个时候parseInt就变成了

function parseInt(currentValue,index) {
    return xxx;
};
["1", "2", "3"].map(parseInt)
            ||
["1", "2", "3"].map(function parseInt(currentValue,index) {
    return xxx;
})
            ||
["1", "2", "3"].map(function callback(currentValue,index) {
    return parseInt(currentValue,index);
})           

currentValue1return parseInt(1,0)
currentValue2return parseInt(2,1)
currentValue3return parseInt(3,2)

在上面我们就分析了

parseInt("1",0);//返回 1
parseInt("2",1);//返回 NaN
parseInt("3",2);//返回 NaN

so 综上所述:["1", "2", "3"].map(parseInt) = [1, NaN, NaN]