Map (parseInt) returns [1, NaN, 3] in Javascript

Translator: War five slag

JavaScript is really weird, don’t believe me? So let’s try to convert an array of strings to an array of integers using map and parseInt. Launch the console on Chrome (win for F12, MAC for Cammand + Option + I), copy and paste the code below, and press Enter to run it.

['1'.'7'.'11'].map(parseInt);
Copy the code

Instead of an integer array [1, 7, 11], we get [1, NaN, 3]. What?? To understand what’s going on, we first need to talk about some JavaScript concepts. If you like TLDR, I wrote a summary at the end of this article.

True and false values

Here’s a simple JavaScript if-else statement:

if (true) {
    // This is always executed
} else {
    // This will never be implemented
}
Copy the code

In this case the judgment condition is always true, the code in if (if-block) is always executed, and the code in else (else block) is never executed. This is easy, isn’t it? Because true is a Boolean, what if we use a non-Boolean value as a condition?

if ('hello world') {
    // Will this still work?
    console.log('The judgment condition is true')}else {
    // Or run here?
    console.log('The judgment condition is false')}Copy the code

Let’s run this code in the console.

We can see that in this case we are actually running if-block because the string hello world is true (note: js does’ implicit conversion ‘).

Every JavaScript object has either a true or false value. When placed in a Boolean context (such as an if-else statement), the object is treated as true or false depending on its authenticity (that is, implicit conversion). So what values are judged to be true and what values are judged false? Follow this simple rule:

All values except the following are judged to be true:false,0,' '(empty string),null,undefinedandNaN

This is confusing. This means that the ‘false’ string false, the ‘0’ string 0, an empty object {}, and an empty array [] are all true values. We can see if this is true by passing these indeterminate objects to Boolean functions (e.g. Boolean(‘0’)).

As far as we’re concerned, we just need to know that 0 will judge to be false

base

0 1 2 3 4 5 6 7 8 9 10
Copy the code

When we count from 0 to 10, each number (0-9) is a separate and distinct number, but once we count to 10, we need two numbers (1 and 0) to represent the number. That’s because we have a decimal base of ten, one for every ten.

The smallest number represented by more than one number. Different bases have different bases, so the same number can represent different sizes in different bases.

Decimal binary hexadecimal RADIX=10 RADIX=2 RADIX=16 000 111 2 10 2 3 11 3 4 100 4 5 101 5 6 110 6 7 111 7 8 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 11Copy the code

For example, if we look at the table above, we see that the number 11 represents different numbers in different bases. Eleven in binary corresponds to three in decimal. If the value is 11 in hexadecimal, the decimal value is 17.

You may have noticed that in our title, parseInt returns 3 when we type 11, which corresponds to the binary on the table, but if you don’t understand, let’s keep going.

Function parameters

In JavaScript, you can call a function with as many arguments as you want. Even if the number of arguments passed is different from the number used in the function declaration, missing arguments are treated as undefined and extra arguments are ignored (but all arguments are stored in the arraylike arguments object).

function foo(x, y) {
    console.log(x)
    console.log(y)
}
foo(1.2);      / / 1. 2
foo(1);         // 1, undefined
foo(1.2.3);   / / 1. 2
Copy the code

Array. The prototype. The map () method

We’re getting to the point!

The map() method is a method on array.prototype that returns a new Array as a result of each element in the Array calling a provided function. For example, the following code multiplies each element in the array by 3:

function multiplyBy3(x) {
    return x * 3
}

const result = [1.2.3.4.5].map(multiplyBy3);

console.log(result);    // [3, 6, 9, 12, 15]
Copy the code

Now if we want to print out every element in the array, we should pass console.log to the map() function, right? !

[1.2.3.4.5].map(console.log)
Copy the code

Miraculously, each console.log prints not only the value of each element, but also the index value of that element and the entire array.

[1.2.3.4.5].map(console.log);

// The above line is equivalent to

[1.2.3.4.5].map(
    (val, index, array) = > console.log(val, index, array)
)

// does not equal

[1.2.3.4.5].map(
    val= > console.log(val)
)
Copy the code

When a function is passed as an argument to map(), three arguments currentValue, currentIndex, and the full array are passed to the function for each iteration. That’s why console.log has three values for each output.

Now we know all the conditions for opening the title puzzle!

Mix these conditions

ParseInt takes two arguments string and radix, and if the radix argument is passed in incorrectly, the radix is set to 10 by default, which is decimal by default.

parseInt('11');         / / 11
parseInt('11'.2);      / / 3
parseInt('11'.16);     / / 17

parseInt('11'.undefined);      // 11
parseInt('11'.0);      // 11
Copy the code

Now let’s run our example step by step

['1'.'7'.'11'].map(parseInt);     // [1, NaN, 3]

/ / the first iteration, the incoming parameters for val = '1', the index = 0, array = [' 1 ', '7', '11']

parseInt('1'.0['1'.'7'.'11']);     / / 1
Copy the code

The second argument is the cardinality. The 0 passed in is false, so it is set to the default decimal. Because parseInt only takes two arguments, the third entire array is ignored. So the string in base 10 of ‘1’ corresponds to the number 1.

/ / the second iteration, access parameters for val = '7', the index = 1, array = [' 1 ', '7', '11']

parseInt('7'.1['1'.'7'.'11']);     // NaN
Copy the code

There is no ‘7’ in base 1. As in the first iteration, the last parameter is ignored. So parseInt returns NaN.

/ / the third iteration, access parameters for val = '11', the index = 2, array = [' 1 ', '7', '11']

parseInt('11'.2['1'.'7'.'11']);     / / 3
Copy the code

This is easy to understand, in base 2 binary, ’11’ corresponds to the number 3, and the last argument is ignored

Summary (TLDR: Too Long, Don’t read)

[‘1’, ‘7’, ’11’].map(parseInt) did not output as we expected because three parameters were passed to parseInt in map in each iteration, and the second parameter index was passed to parseInt as the radix parameter. Therefore, a different base is used to parse the array so that each string ’11’ is parsed to base 2 i.e. binary, so 3 is returned. ‘7’ is parsed to base 1, so NaN is returned, the index of ‘1’ is 0,0 is false, so it is reset to decimal, so 1 is returned again.

So if we want to do what we want to do, we have to write it this way

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

// Pass in an argument, and it's ok
Copy the code

I’m a front end warrior, a front end elementary school.