Array.prototype.map的一道经典面试题

一、前言

面试题:[1,2,3].map(parseInt)输出结果是什么

你的答案是什么?

二、两个方法

由本题引申出两个js方法的细节,分别是数组map方法和parseInt方法,两个熟悉到不能在熟悉的方法,放在一起却翻了船

1. map

函数说明:

map方法会给原数组中的每个元素都按顺序调用一次 callback 函数。callback 每次执行后的返回值(包括 undefined)组合起来形成一个新数组。

函数签名

1
2
3
var newArr = arr.map(function(currentValue, index, arr) {
// each item will processed by this function
}, thisArg)

参数说明

该方法有两个参数,第一个是回调函数,第二个在回调函数中this的指向

第一个参数回调函数接受三个参数,第一个是当前正在遍历的数组元素(必须),第二个参数是当前元素的索引值(非必须),第三个元素是数组本身(非必须)(引用)

第二个参数是thisArg,用于指定回调函数中this的指向(上下文环境)

注意点:

  1. map会自动传递三个参数,分别是当前正在遍历数组元素,当前元素索引,数组本身

  2. map函数不是原地算法,必须用一个新数组接受map方法的返回值

  3. map遍历的数组在调用时就已经确定,即使在回调函数中增加或者减少数组项,也不会影响遍历次数。如果增加元素不会遍历到

    1
    2
    3
    4
    5
    6
    let arr = [1, 2, 3, 4, 5, 6];
    let res = arr.map((item, index, ref) => {
    ref.push(1);
    return item;
    })
    console.log(res); // 1,2,3,4,5,6

    如果删除元素,则遍历的时候会访问不了元素

    1
    2
    3
    4
    5
    6
    let arr = [1, 2, 3, 4, 5, 6];
    let res = arr.map((item, index, ref) => {
    ref.splice(0,1);
    return item;
    })
    console.log(res); // 1,3,5,empty,empty,empty
  4. 如果回调函数中没有返回, 则默认值为undefined

2. parseInt

函数说明

根据指定进制解析字符串,并返回十进制表示的整数

函数签名

1
function parseInt(str, radix) {}

参数说明

两个参数,第一个是要解析的值(必须),如果不是字符串会调用toString方法转换字符串

第二个参数是使用什么进制(非必须),默认不是 十进制,范围为2-36

默认不是十进制是引用的MDN文档,刚看到其实有点疑问,为什么不是十进制,我日常用这个方法,都不会指定第二个参数(进制数),解析也都是用的十进制。
看过后面的解析渐渐明白,默认确实不是十进制,而是根据你要解析的字符串确定的,具体解释如下

如果没有传进制,主要分两情况

  1. 如果要解析的字符串以 0x 或者 0X 开头,则以16进制进行解析
  2. 如果要解析的字符串以 0 开头,则以八进制或者十进制进行解析,在ECMAScript5中指明要以十进制进行解析,但浏览器兼容存在问题,最好指定第二个参数
  3. 其余情况默认按照十进制解析

如果传递了第二个参数

  1. 如果第二个参数为 undefined0null 则以十进制进行解析
  2. 如果进制数小于 2 或大于 36 ,则返回NaN
    解析过程如下图

parseInt解析过程

这里的第一步,也就是先尝试解析第一个参数这一步是我自己的理解,没有看过源码,感觉这样可能比较合理,先考虑边界情况。

注意

  1. 在解析过程中,如果遇到字符串不属于指定进制中的数字,则解析终止,只返回已解析的部分
    1
    2
    parseInt("123a",10) // 遇到a,解析停止,返回123
    parseInt("123a123",10) // 遇到a,解析停止,后面部分不会解析,返回123
  2. 在转换bigInt的时候可能会出现精度丢失的问题,因为bigInt最后会有一个n的拖尾,在解析时被丢失
  3. parseInt可以理解正负号,如果待解析字符串带有符号,则会记录符号,并从非符号位开始解析

三、解析

题目[1,2,3].map(parseInt)
根据map特性 map会自动传入三个参数 ,代码其实本来面目是这样

1
2
3
[1,2,3].map(function(item, index, arr){
return parseInt(item, index, arr);
})

逐个解析,执行三次循环,过程应该是这样

  1. 第一次循环
    1
    parseInt(1, 0, [1,2,3])
    第三个参数被忽略,要解析的字符串为数字1,先调用toString转字符串,第二个参数为0,根据解析规则,应该按照十进制进行解析,所以结果为1,见parseInt参数说明第三条
  2. 第二次循环
    1
    parseInt(2, 1, [1,2,3])
    基本同上,第二个参数为1,直接返回NaN,见parseInt参数说明第四条
  3. 第三次循环
    1
    parseInt(3, 2, [1,2,3])
    超出二进制能表示的范围,第一位数字无法解析,返回NaN,见parseInt参数说明第四条
    综上,返回结果为【1,NaN,NaN】