DEV Community

Alessio Michelini
Alessio Michelini

Posted on

You should stop using `parseInt()`

This is something I see all the times, you have some code where at some point you have a variable that contains a number, maybe it comes from a form, or from the results of some API calls, or whatever, and you want convert it into an integer.

Something like this for example:

const myNumber = '1';

if (parseInt(myNumber, 10) === 1) {
  // do something
}
Enter fullscreen mode Exit fullscreen mode

While the code above works, it’s an extremely inefficient way to parse it.
What you should use instead is the Number() function, and convert the code above to this:

const myNumber = '1';

if (Number(myNumber) === 1) {
  // do something
}
Enter fullscreen mode Exit fullscreen mode

From the changes above you gain two things:

  • a more readable code
  • a much more performing way to transform a string to an integer

But what’s the difference between Number and parseInt?

The Number(string) function evaluate the full string and converts it to a string, and if the string is not a number it will just return NaN.
While parseInt(string, [radix]) will try to find the first number in the string passed, and convert it to the radix passed, which is 10 by default, and it will return NaN only if it doesn’t find any number.

This means that if you pass a string like 5e2 , it parseInt will stop when it sees the e and it will just return 5, while Number will evaluate the whole string and return the correct value 500.

Here you can see some cases compared between the two functions:

console.log(Number('a')); // NaN
console.log(Number('1')); // 1
console.log(Number('5e2')); // 500
console.log(Number('16px')); // NaN
console.log(Number('3.2')); // 3.2

console.log(parseInt('a')); // NaN
console.log(parseInt('1')); // 1
console.log(parseInt('5e2')); // 5
console.log(parseInt('16px')); // 16
console.log(parseInt('3.2')); // 3
Enter fullscreen mode Exit fullscreen mode

It’s also a matter of execution time

Maybe you are still undecided, and you think that “I just need to convert a simple number to an integer, why I should use Number instead?”.
Well, because of performances.

For example, let’s do a simple function, that loops for 100m times, and that accepts a callback, and we call it twice using Number in the first case and parseInt in the second.

function runBench(cb) {
  const start = new Date();
  for (let i = 0; i < 100000000; i++) {
    cb();
  }
  const end = new Date();
  console.log(`It took ${end - start} ms`);
}

const process1 = () => Number('3.2');
const process2 = () => parseInt('3.2', 10);

runBench(process1); // It took 140 ms
runBench(process2); // It took 4546 ms
Enter fullscreen mode Exit fullscreen mode

Sure, you are not going to run a loop of 100 millions, but it’s to make evident the performance difference between the two functions, and also when you use parseInt in multiple places on the same function, things might just sum up at the end.

So should I just avoid parseInt all the times?

No, not always, there are of course use cases where it’s beneficial to use it, for example if you want to extrapolate an integer out of a floating number, which is a good 50% faster than Math.round().
For example ifyou want to convert a string with pixels on it to just a number, like 32px to 32, then you should use parseInt, but most of the times you better stick with Number instead.
Or even if you want to convert a number from a decimal system to something else.

Conclusions

Unless some specific cases, where parseInt returns what you need and Number doesn’t, for 99% of the cases you should better start to use the latter one.

Update: a few more benchmarks

Just to give a broader picture as there are more ways to convert a string to a number, I also added tests using parseFloat and the Unary operator, here there results:

function runBench(cb) {
  const start = new Date();
  for (let i = 0; i < 100000000; i++) {
    cb();
  }
  const end = new Date();
  console.log(`It took ${end - start} ms`);
}

const process1 = () => Number('1');
const process2 = () => parseInt('1', 10);
const process3 = () => parseFloat('1');
const process4 = () => +'1';

runBench(process1); // It took 70 ms
runBench(process2); // It took 4552 ms
runBench(process3); // It took 5082 ms
runBench(process4); // It took 412 ms
Enter fullscreen mode Exit fullscreen mode

As you can see above, using the Number() is still the fastest way to do the conversion.

Discussion (42)

Collapse
ky1e_s profile image
Kyle Stephens • Edited on

Unless you're doing this in the order of hundreds of thousands, performance is neglible (I've rarely come across use cases like this in prod systems)

While it's nice to think about these things and how you write code, I will always push back on people looking to enforce issues or 'standards' like this in PRs because they're rather subjective POV on style or rely on contrived performance benefits. If this seems like a harsh response, I apologise, but I'm wary of articles with titles like you 'should' be doing this, or you 'must not' do this, etc.

Collapse
leouofa profile image
Leonid Medovyy

I generally agree, with that said. If you’re doing particle based canvas art the execution time will make a difference in the overall performance of the artwork. It’s probably relevant if you’re doing 3D as well.

Collapse
maxziebell profile image
Max Ziebell

This is so true. Instead of pushing back just add a math.round to the Number() and the readability suffers and probably the performance too. I agree, the suggested „optimization“ is not worth implementing and worth thinking about in most real world use cases.

Collapse
lionelrowe profile image
lionel-rowe • Edited on

there are of course use cases where it’s beneficial to use it, for example if you want to extrapolate an integer out of a floating number, which is a good 50% faster than Math.round().

Two things here -

  1. parseInt doesn't do any rounding, only truncation. The equivalent Math function would be Math.trunc:

    Math.round('0.999') // 1
    Math.trunc('0.999') // 0
    parseInt('0.999')   // 0
    
    Math.round('-0.999') // -1
    Math.trunc('-0.999') // -0
    parseInt('-0.999')   // -0
    
  2. Running benchmarks in Chrome, the performance benefit of parseInt over Math.round is reversed if you explicitly convert to a number first:

    const bench = (desc, cb) => {
        const start = new Date()
    
        for (let i = 0; i < 1e7; ++i) {
            cb()
        }
    
        const end = new Date()
    
        console.log(desc, `${end - start} ms`)
    }
    
    bench('parseInt',       () => parseInt('3.2', 10))       // 374 ms
    bench('round coerced',  () => Math.round('3.2'))         // 738 ms
    bench('round explicit', () => Math.round(Number('3.2'))) //  70 ms
    bench('trunc coerced',  () => Math.trunc('3.2'))         // 671 ms
    bench('trunc explicit', () => Math.trunc(Number('3.2'))) //  62 ms
    
Collapse
ilumin profile image
Teerasak Vichadee

Kudos!

Collapse
darkmavis1980 profile image
Alessio Michelini Author

Good to know, thanks!

Collapse
andreidascalu profile image
Andrei Dascalu

Ok, but you did sum up the issue yourself in the examples. ParseInt will order to an integer whereas Number will get the proper numeric representation. Which means the outcome ma not be an int, which is what ParseInt guarantees .
The use of ParseInt can be replaced with Number if and only if it's accompanied by Math.round. This is not an edge case or an odd use case, it's the missing piece to have the intended outcome: an integer. The odd case is requiring the use of radix.

Collapse
darkmavis1980 profile image
Alessio Michelini Author

True, in fact if you want to take a string that could contain a float, and you want an integer, in that case you want to use that, but I'm not talking about that case, I'm talking about having the actual number correctly translated from a string to a number, which is probably the most common case.

Collapse
andreidascalu profile image
Andrei Dascalu

My point is mostly:

  • the performance comparison is moot because the functions discussed don't do the same thing
  • the correct way to formulate the use case for Number is: you should use Number when you want to extract the correct numeric value in full from a string AND you don't care about the resulting type.
  • use case for ParseInt: you want to ensure conversion to an integer OR the partial extraction of an integer (similar for float)
Collapse
andreidascalu profile image
Andrei Dascalu

The most common case for ParseInt is to get the correct number from a string with the even expectation of getting a float .... from a function that has int in the name? I hardly believe that.
I have never seen in 20 years a case of parseInt used with the expectation of getting anything except an int.

Thread Thread
darkmavis1980 profile image
Alessio Michelini Author

Unfortunately I see it using it for what Number is supposed to do, many, many times.
Btw, if you just need to get the integer (that is a positive number) from a string containing a float, then you should use double tilde operator, which does the same as Math.floor, but just 10 times faster (in Node at least), for example:

console.log(~~'3.2'); // returns 3
console.log(parseInt('3.2')); // returns 3
Enter fullscreen mode Exit fullscreen mode
Thread Thread
markgoho profile image
Mark Goho

double tilde...🤯

Collapse
taufik_nurrohman profile image
Taufik Nurrohman • Edited on
const myNumber = '1';
console.log(+myNumber);
Enter fullscreen mode Exit fullscreen mode
const myNumber = '1.5';
console.log(+myNumber);
Enter fullscreen mode Exit fullscreen mode

Shorter.

By the way, you will need parseInt anyway, if you want to deal with custom base number other than 10. Example is to convert HEX color code into RGB color code:

console.log([
    parseInt('ff', 16),
    parseInt('a5', 16),
    parseInt('00', 16)
]);
Enter fullscreen mode Exit fullscreen mode
Collapse
coolprofessor profile image
coolprofessor

What about eval()?

Collapse
blackr1234 profile image
blackr1234 • Edited on

It will be an overkill. There are so many working ways of parsing integers. Why bother using such a dangerous way?

Collapse
aminmansuri profile image
hidden_dude

eval is dangerous..

eval("alert('you are hacked')")

Collapse
coolprofessor profile image
coolprofessor • Edited on

I get your point, but you can already run JS commands in the console. Also, you can use str.includes("()").

Thread Thread
blackr1234 profile image
blackr1234

However, end users being able to use console to execute any code doesn't necessarily mean that they will want to do it proactively. If you use eval and if the input is harmful, the end user may be passively affected.

Thread Thread
coolprofessor profile image
coolprofessor

Potentially, but can't you check the string for functions using 'str.includes("()")'?

Thread Thread
blackr1234 profile image
blackr1234 • Edited on

If you are referring to checking if the string contains function call by searching for "()", no it won't work because there are way too many scenarios. Consider a case when there are spaces in between the parenthesis, e.g. foo( ) and your code will then allow it to run. It will be better if you only allow whitelisted characters. However, it will still take unnecessary effort and still potentially cause the program to hang (if you are going to search/parse the whole string which can be very long). So just use the built-in functions that work just fine and don't reinvent the wheel, which is something stupid.

Collapse
aminmansuri profile image
hidden_dude

Don't do it.

eval() should never be used on user input.

Often parsing strings to Int is done for security reasons. Using eval() would just lead you to code injection and XSS problems.

Don't do it!

Collapse
receter profile image
Andreas Riedmüller

Keep in mind that both return different results when when passing nullish or boolean values:

parseInt(); // NaN
parseInt(null); // NaN
parseInt(''); // NaN
parseInt(true); // NaN

Number(); // 0
Number(null); // 0
Number(''); // 0
Number(true); // 1
Enter fullscreen mode Exit fullscreen mode
Collapse
jonrandy profile image
Jon Randy

Or use +

Collapse
darkmavis1980 profile image
Alessio Michelini Author

But thanks for pointing it out, I've updated the article adding the test results using the unary operator and parseFloat

Collapse
darkmavis1980 profile image
Alessio Michelini Author

with the unary operator is still 5/6 times slower than Number

Collapse
jonrandy profile image
Jon Randy

Interesting - I tested on Firefox and using the unary + was almost twice as fast as Number - jsbench.me/v2kurpao3r/1

Thread Thread
jonrandy profile image
Jon Randy • Edited on

The same bench on Chrome showed the two methods almost exactly the same speed - sometimes one would be faster than the other, sometimes not

Thread Thread
darkmavis1980 profile image
Alessio Michelini Author

This is interesting, I run the tests using Node (v14.17.6), in "theory" it should give you similar results to Chrome as they both use V8 as the engine, but it's clearly different

Collapse
domagojvidovic profile image
Domagoj Vidovic

This is cool, thanks for sharing.

Btw. for measuring the performance use performance.now() instead of Date, it's more precise.

Collapse
darkmavis1980 profile image
Alessio Michelini Author

Thanks, forgot about that one!

Collapse
patarapolw profile image
Pacharapol Withayasakpunt

Why not compare to parseFloat?

Anyway, parseInt forgivingness is so bad.

Collapse
darkmavis1980 profile image
Alessio Michelini Author • Edited on

parseFloat is even worst than parseInt if you just want to convert a string to a number

Collapse
jonosellier profile image
jonosellier

FYI, Math.round() will round the number, not chop off the decimal. Use floor or better yet |0 to drop the decimal

Collapse
jessekphillips profile image
Jesse Phillips

5e2[...] Number will evaluate the whole string and return the correct value 500.

Are you saying it will return an http code of 500?

Collapse
darkmavis1980 profile image
Alessio Michelini Author

ehm, no? :D

Collapse
jessekphillips profile image
Jesse Phillips

Strange, it looked like something had gone horribly wrong if you got 500 out of 1506.

I can at least see how an algorithm can utilize 5, but 500 is mind-boggling wrong.

Collapse
kigiri profile image
Clément

One thing to note that caugth me by surprise is that Number will parse any falsy value as 0, so undefined, null, false all parse fine and return 0, can be unexpected.

Collapse
mhoumann87 profile image
Michael Houmann

Why not save some time and use the simple '+' before the string you want to parse? Same result as Number, but quite faster to write

Collapse
maorkavod profile image
maorkavod • Edited on

You can also use this :

~~'234';

or even :

'234' | 0;

Collapse
avngarde profile image
Kamil Paczkowski

Cool, i didnt knew about it!

Collapse
talorlanczyk profile image
TalOrlanczyk

Just small update instead of Number(something)
you can write +something and it will convert it to number

Collapse
darkmavis1980 profile image
Alessio Michelini Author

I know, it's on the article as well, it's called unary operator ;-)