阅读 1592

为什么["1","2","3"].map(parseInt) 返回[1,NaN,NaN]?

在 javascript 中 ["1","2","3"].map(parseInt) 为何​返回不是 [1,2,3] 却是 [1,NaN,NaN]

我们使用进制的相关知识来解释说明,如果你看完还是没有很清楚,可以自己去了解一些进制的相关知识。

我们首先回顾一下 parseInt()map() 两个函数的用法:

1、parseInt() 函数:

parseInt() 函数可解析一个字符串,并返回一个整数,可有两个参数。

//语法:
parseInt(string, radix)
复制代码
参数描述
string必需。要被解析的字符串。
radix

可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。

如果省略该参数或其值为 0,则数字将以 10 为基础来解析。

1、当参数 radix 的值为 0,或没有设置该参数时,parseInt() 会根据 string 来判断数字的基数。

当忽略参数 radix , JavaScript 默认数字的基数如下:

(1)如果 string 以 "0x"或 “0X” 开头,parseInt() 会把 string 的其余部分解析为十六进制的整数。

(2)如果 string 以 0 开头,那么 ECMAScript v3 允许 parseInt() 的一个实现把其后的字符解析为八进制或十六进制的数字。

(3)如果 string 以 1 ~ 9 的数字开头,parseInt() 将把它解析为十进制的整数。

2、如果该参数小于 2 或者大于 36,则返回 NaN

提示和注释

(1)只有字符串中的第一个数字会被返回。

(2)开头和结尾的空格是允许的。

(3)如果字符串的第一个字符不能被转换为数字,则返回 NaN。

(4)在字符串以"0"为开始时旧的浏览器默认使用八进制基数。ECMAScript 5,默认的是十进制的基数。

//当忽略参数 radix 
parseInt("10")//1*10^1+0*10^0=10  
parseInt("10.33")//1*10^1+0*10^0=10 
parseInt("34 45 66") //3*10^1+4*10^0=34 
parseInt(" 60 ") //6*10^1+0*10^0=60 
parseInt("40 years")  //4*10^1+0*10^0=40 
parseInt("He was 40")  //NaN 
parseInt("1f",16);		//1*16^1+f*16^0=16+15=31
parseInt("0x10")//1*16^1+0*16^0=16  string 以 "0x" 开头,会把 string 的其余部分解析为十六进制的整数 
parseInt("010")//0*10^2+1*10^1+0*10^0=10 或  0*8^2+1*8^1+0*8^0=8  string 以 0 开头,旧浏览器默认使用八进制基数。ECMAScript 5,默认的是十进制的基数。

//当有参数 radix,表示以radix为基数解析
parseInt("10",10) //1*10^1+0*10^0=10 
parseInt("10",8)//1*8^1+0*8^0=8
parseInt("10",16) //1*16^1+0*16^0=16复制代码

我们还需要了解进制数的相关知识:

二进制的定义:用0和1两个数码来表示的数。

三进制的定义:用0和1,2三个数码来表示的数......

十进制的定义:用0和1,2,3...9十个数码来表示的数。

十进制分别转为二、三、四进制的值
十进制二进制三进制四进制
0000
1111
21022
311103
41001110
51011211
61102012
71112113
810002220
9100110021
10101010122
//以2为基数,
parseInt('1', 2)//1
parseInt('2', 2)//NaN
parseInt('3', 2)//NaN
parseInt('10',2)//2
//以3为基数,
parseInt('1', 3)//1
parseInt('2', 3)//2
parseInt('3', 3)//NaN
parseInt('10',3)//3
//以4为基数,
parseInt('1', 4)//1
parseInt('2', 4)//2
parseInt('3', 4)//3
parseInt('4', 4)//NaN
parseInt('10',4)//4
......

//总结:
我们可以这样理解,二进制里没有2,3,...9,三进制里没有3,4...9,四进制里没有4,5...9
即当string无法转成radix基数时会返回NaN
复制代码

由上可知,相关文档只说明了两种情况会返回NaN,实际上还存在上面代码的情况:当string无法转成radix进制数时会返回NaN

但是为什么 ["1","2","3"].map(parseInt) 却返回 [1,NaN,NaN]parseInt 需要 2 个参数 (val, radix), 而 map 传递了 3 个参数 (element, index, array)。

回到原题,做出解释:

['1','2','3'].map(parseInt)
//  parseInt('1', 0) -> 1   //radix=0,以10为基数解析 1*10^1+0*10^0=10
//  parseInt('2', 1) -> NaN //radix=1<2,返回NaN
//  parseInt('3', 2) -> NaN //3无法转成二进制

//即遍历后结果为 [1,NaN,NaN]
复制代码

以上为题的正解说明。

我们接着来说map( )的用法:

2、map()方法:

对数组的每个元素调用定义的回调函数并返回包含结果的数组。

//语法:
array.map(function(currentValue,index,arr), thisValue)
复制代码
参数描述
function(currentValue, index,arr)
必须。函数,数组中的每个元素都会执行这个函数
函数参数:
参数描述
currentValue
必须。当前元素的值
index
可选。当前元素的索引值
arr
可选。当前元素属于的数组对象
thisValue
可选。对象作为该执行回调时使用,传递给函数,用作 "this" 的值。
如果省略了 thisValue ,"this" 的值为 "undefined"

返回值:其中的每个元素均为关联的原始数组元素的回调函数返回值的新数组。

对于数组中的每个元素,map 方法都会调用 callbackfn 函数一次(采用升序索引顺序)。 不为数组中缺少的元素调用该回调函数。

下面的示例阐释了 map 方法的用法:

// 定义回调函数
// 计算圆的面积
function AreaOfCircle(radius) { 
    var area = Math.PI * (radius * radius); 
    return area.toFixed(0); 
} 
// 定义一个数组,保护三个元素
var radii = [10, 20, 30]; 
// 计算 radii 的面积. 
var areas = radii.map(AreaOfCircle); // 314,1257,2827复制代码

下面的示例阐释thisValue参数的用法,该参数指定对其引用 this 关键字的对象,相当于给map的回调函数指定了this对象:

// 定义一个对象 object,保护 divisor 属性和 remainder 方法
// remainder 函数求每个传入的值的个位数。(即除以 10 取余数)
var obj = { 
    divisor: 10, 
    remainder: function (value) { 
        return value % this.divisor; 
    } 
} 
var numbers = [6, 12, 25, 30]; 

divisor = 6;
function remainder(value) {
return value % this.divisor;
}

numbers.map(obj.remainder, obj); //[6,2,5,0] obj.remainder this=obj
numbers.map(remainder, obj);//[ 6,2,5,0] window.remainder this=obj
numbers.map(remainder);//[0,0,1,0] window.remainder this=window 
numbers.map(obj.remainder);//[0,0,1,0] obj.remainder this=window 
//有两个remainder函数,第一步确定调用的是哪一个,第二步确定执行的时候this是谁复制代码
重新定义 parseInt(string, radix) 函数:
var parseInt = function(string, radix) {
    return string + "-" + radix;
};
 
["1", "2", "3"].map(parseInt);//["1-0", "2-1", "3-2"]复制代码
由此可见,map 函数将数组的值 value 传递给了 parseInt 的第一个参数,将数组的索引传递给了第二个参数。 第三个参数呢?我们再加一个参数:
var parseInt = function(string, radix, obj) {
    return string + "-" + radix + "-" + obj;
};

["1", "2", "3"].map(parseInt);//["1-0-1,2,3", "2-1-1,2,3", "3-2-1,2,3"]
//"-" + obj 导致obj调用toString()方法自动转换为了string,相当于连接符
//第1次遍历:1 + "-" + 0 + "-" + [1,2,3]="1-0-1,2,3"

//toString()还有以下几种特殊情况
[{a:1},{b:2}].toString()//"[object Object],[object Object]"
[1,2].toString()//"1,2"
[[1,2],[3,4]].toString()//"1,2,3,4"复制代码