39

为什么 ['1', '7', '11'].map(pars...

 4 years ago
source link: https://www.tuicool.com/articles/MNBj6rN
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Javascript 总是以超自然的方式执行我们的代码,这是一件很神奇的事情,如果不信的话,思考一下 ['1', '7', '11'].map(parseInt) 的结果是什么?你以为会是 [1, 7, 11] 吗?我都这么问了,那肯定不是:

['1', '7', '11'].map(parseInt)
// 输出:(3) [1, NaN, 3]

要理解为什么为会这里,首先需要了解一些 Javascript 概念,如果你是一个不太喜欢阅读长文的人,那直接跳到最后看结论吧

真值与假值

请看下面示例:

if (true) {
  // 这里将启动都会被执行
} else {
  // 这里永远都不会被执行
}

在上面的示例中, if-else 声明为 true ,所以 if-block 会一直都执行,而 else-block 永远都会被忽略掉,这是个很容易理解的示例,下面我们来看看另一个例子:

if ("hello world") {
  // 这里会被执行吗?
  console.log("条件判断为真");
} else {
  // 或者会执行这里吗?
  console.log("条件判断为假");
}

打开你的开发者工具 console 面板,执行上面的代码,你会得到看到会打印出 条件判断为真 ,也就是说 "hello world" 这一个字符串被认为是 真值

在 JavaScript 中,truthy(真值)指的是在布尔值上下文中,转换后的值为真的值。所有值都是真值,除非它们被定义为 假值(即除 false、0、""、null、undefined 和 NaN 以外皆为真值)。

引用自: Mozilla Developer: Truthy(真值)

这里一定要划重点: 在布尔上下文中,除 false0"" (空字符串)、 nullundefined 以及 NaN 外,其它所有值都为真值

基数

0 1 2 3 4 5 6 7 8 9 10

当我们从 0 数到 9 ,每一个数字的表示符号都是不一样的(0-9),但是当我们数到 10 的时候,我们就需要两个不同的符号 10 来表示这个值,这是因为,我们的数学计数系统是一个十进制的。

基数,是一个进制系统下,能使用仅仅超过一个符号表示的数字的最小值, 不同的计数进制有不同的基数,所以,同一个数字在不同的计数体系下,表示的真实数据可能并不一样 ,我们来看一下下面这张在十进制、二进制以及十六进制不同值在具体表示方法:

十进制(基数:10) 二进制(基数:2) 十六进制(基数:16) 0 0 0 1 1 1 2 10 2 3 11 3 4 100 4 5 101 5 6 110 6 7 111 7 8 1000 8 9 1001 9 10 1010 A 11 1011 B 12 1100 C 13 1101 D 14 1110 E 15 1111 F 16 10000 10 17 10001 11 18 10002 12

你应该已经注意到了, 11 在上表中,总共出现了三次。

函数的参数

在 Javascript 中,一个函数可以传递任何多个数量的参数,即使调用时传递的数量与定义时的数量不一致。缺失的参数会以 undefined 作为实际值传递给函数体,然后多余的参数会直接被忽略掉(但是你还是可以在函数体内通过一个类数组对象 arguments 访问到)。

function sum(a, b) {
  console.log(a);
  console.log(b);
}

sum(1, 2);    // 输出:1, 2
sum(1);       // 输出:1, undefined
sum(1, 2, 3); // 输出:1, 2

map()

快要有结果了。

map() 是一个存在于数组原型链上的方法,它将其数组实例中的每一个元素,传递给它的第一个参数(在本文最开始的例子中就是 parseInt ),然后将每一个返回值都保存到同一个新的数组中,遍历完所有元素之后,将新的包含了所有结果的数组返回。

function multiplyBy3 (number) {
  return number * 3;
}

const result = [1, 2, 3, 4, 5].map(multiplyBy3);

console.log(result); // 输出:[3, 6, 9, 12, 15]

现在,假想一下,我们想使用 map() 将一个数组中的每一个元素都打印到控制台,我可以直接将 console.log 函数传递给 map() 方法:

[1, 2, 3, 4, 5].map(console.log);

输出:

1 0 (5) [1, 2, 3, 4, 5]
2 1 (5) [1, 2, 3, 4, 5]
3 2 (5) [1, 2, 3, 4, 5]
4 3 (5) [1, 2, 3, 4, 5]
5 4 (5) [1, 2, 3, 4, 5]

是不是感觉有点神奇了?为什么会这样?来分析一下上面输出的内容都有些什么?

0
(5) [1, 2, 3, 4, 5]

再来看看下面这段代码:

[1, 2, 3, 4, 5].map((value, index, array) => console.log(value, index, array));

在控制台上试试执行上面这行代码,你会发现,它与 [1, 2, 3, 4, 5].map(console.log); 输出的结果是完全一致的,**总是会被我们忽略的一点是, map() 方法会将三个参数传递传递给它的函数,分别是 currentValue (当前值)、 currentIndex (当前索引)以及 array 本身,这就是,上面为什么结果有三列的原因,如果我们只是想将每一个值打印,那么需要像下面这样写:

[1, 2, 3, 4, 5].map((value) => console.log(value));

回到最开始的问题

现在让我们来回顾一下本文最开始的问题:

为什么 ['1', '7', '11'].map(parseInt) 的结果是 [1, NaN, 3]

来看看 parseInt() 是一个什么样的函数:

parseInt(string, radix) 将一个字符串 string 转换为 radix 进制的整数, radix 为介于 2-36 之间的数。

详情: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/parseInt

这里还有一条需要注意的是,虽然我们一般都是使用 parseInt('11') 这样的调用方式,认为是将 11 按十进制转换成数字,但是,请在使用的时候,永远都添加 radix 参数,因为, radix10 并不保证永远有效(这并不是规范的一部分)。

那么看看下面这些的输出:

parseInt('11');             // 输出:11
parseInt('11', 2);          // 输出:3
parseInt('11', 16);         // 输出:17

parseInt('11', undefined);  // 输出:11 (radix 是假值)
parseInt('11', 0);          // 输出:11 (radix 是假值)

现在,让我们一步一步来看 ['1', '7', '11'].map(parseInt) 的整个执行过程,首先,我们可以将这一行代码,转换为完整的版本:

['1', '7', '11'].map((value, index, array) => parseInt(value, index, array))

根据前面的知识,我们应该知道, parseInt(value, index, array) 中的 array 会被忽略(并放入 arguments 对象中。

那么,完整的代码就是下面这样的:

['1', '7', '11'].map((value, index, array) => parseInt(value, index))
  • 遍历到第一个元素时:

    parseInt('1', 0);
radix` 为 `0`,是一个假值,在我们的控制台中,一般将使用 `10` 进制,所有,得到结果 `1`
  • 遍历到第二个元素时:

    parseInt('7', 1)
在一个 `1` 进制系统中,`7` 是不存在的,所以得到结果 `NaN`(不是一个数字)
  • 遍历到第三个元素时:

    parseInt('11', 2)
在一个 `2` 进制系统中,`11` 就是进十制的 `3`

总结

['1', '7', '11'].map(parseInt) 的结果是 [1, NaN, 3] 的原因是因为, map() 方法是向传递给他的函数中传递三个参数,分别为当前值,当前索引以及整个数组,而 parseInt 函数接收两个参数:需要转换的字符串,以及进制基数,所以,整个语句可以写作: ['1', '7', '11'].map((value, index, array) => parseInt(value, index, array))arrayparseInt 舍弃之后,得到的结果分别是: parseInt('1', 0)parseInt('7', 1) 以及 parseInt('11', 2) ,也就是上面看到的 [1, NaN, 3]

正确的写法应该是:

['1', '7', '11'.map(numStr => parseInt(numStr, 10));

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK