parseInt vs unary plus, ¿cuándo usar cuál?

149

¿Cuáles son las diferencias entre esta línea?

var a = parseInt("1", 10); // a === 1

y esta linea

var a = +"1"; // a === 1

Esta prueba jsperf muestra que el operador unario es mucho más rápido en la versión actual de Chrome, suponiendo que sea para node.js !?

Si trato de convertir cadenas que no son números, ambos regresan NaN:

var b = parseInt("test" 10); // b === NaN
var b = +"test"; // b === NaN

Entonces, ¿cuándo debería preferir usar parseIntsobre el plus unario (especialmente en node.js) ???

editar : ¿y cuál es la diferencia con el operador de doble tilde ~~?

hereandnow78
fuente
1
Benchmark jsperf.com/parseint-vs-unary-operator
Roko C. Buljan

Respuestas:

169

Consulte esta respuesta para ver un conjunto más completo de casos.




Bueno, aquí hay algunas diferencias que conozco:

  • Una cadena vacía se ""evalúa como a 0, mientras que se parseIntevalúa como NaN. En mi opinión, una cadena en blanco debe ser a NaN.

    +'' === 0;              //true
    isNaN(parseInt('',10)); //true
  • El unario +actúa más como parseFloatya que también acepta decimales.

    parseIntpor otro lado, deja de analizar cuando ve un carácter no numérico, como el punto que pretende ser un punto decimal ..

    +'2.3' === 2.3;           //true
    parseInt('2.3',10) === 2; //true
  • parseInty parseFloatanaliza y construye la cadena de izquierda a derecha . Si ven un carácter no válido, devuelve lo que se ha analizado (si lo hay) como un número, y NaNsi ninguno se ha analizado como un número.

    El unario, +por otro lado, volverá NaNsi la cadena completa no es convertible a un número.

    parseInt('2a',10) === 2; //true
    parseFloat('2a') === 2;  //true
    isNan(+'2a');            //true
  • Como se ve en el comentario de @Alex K. , parseInty parseFloatse analizará por carácter. Esto significa que las notaciones hexadecimales y exponentes fallarán ya que xy ese tratan como componentes no numéricos (al menos en base10).

    Sin +embargo, el unario los convertirá correctamente.

    parseInt('2e3',10) === 2;  //true. This is supposed to be 2000
    +'2e3' === 2000;           //true. This one's correct.
    
    parseInt("0xf", 10) === 0; //true. This is supposed to be 15
    +'0xf' === 15;             //true. This one's correct.
Joseph
fuente
66
También cuando se usa una raíz+"0xf" != parseInt("0xf", 10)
Alex K.
Me gusta su respuesta hasta ahora, ¿puede explicar también cuál es la diferencia con el operador de doble tilde ~~?
hereandnow78
@ hereandnow78 Eso se explicaría aquí . Es el equivalente bit a bit de Math.floor(), que básicamente corta la parte decimal.
Joseph
44
En realidad, "2e3"no es una representación entera válida para 2000. Sin embargo, es un número válido de coma flotante: parseFloat("2e3")rendirá correctamente 2000como respuesta. Y "0xf"requiere al menos una base 16, razón por la cual parseInt("0xf", 10)devuelve 0, mientras que parseInt("0xf", 16)devuelve el valor de 15 que esperaba.
Bart
2
@Joseph the Dreamer y @ hereandnow78: Double tilde corta la parte decimal del número, mientras que Math.floor devuelve el número inferior más cercano. Funcionan igual para un número positivo, pero Math.floor(-3.5) == -4y ~~-3.5 == -3.
Albin
261

La mejor tabla de conversión de cualquier número a número: Tabla de conversión

georg
fuente
2
Por favor agregue "NaN"a esta tabla.
chharvey
Puede valer la pena agregar una isNaNcolumna a esta tabla: por ejemplo, isNaN("")es falsa (es decir, se considera un número), pero parseFloat("")es NaN, lo que puede ser un problema, si está intentando usar isNaNpara validar la entrada antes de pasarla aparseFloat
Retsam
También debe agregar '{valueOf: function(){return 42}, toString: function(){return "56"}}'a la lista. Los resultados mixtos son interesantes.
murrayju
3
Entonces, el resumen de la tabla es que +es solo una forma más corta de escribir Number, y las más avanzadas son formas locas de hacerlo que fallan en los casos extremos.
Mihail Malostanidis
¿Es [] .undef una cosa, o es solo una forma arbitraria de generar indefinido? No se puede encontrar ningún registro de "undef" relacionado con JS a través de Google.
jcairney
10

La tabla en la respuesta de thg435 creo que es completa, sin embargo, podemos resumir con los siguientes patrones:

  • Unary plus no trata todos los valores falsos de la misma manera, pero todos salen como falsos.
  • Unary plus envía truea 1, pero "true"a NaN.
  • Por otro lado, parseIntes más liberal para cadenas que no son dígitos puros. parseInt('123abc') === 123, mientras que los +informes NaN.
  • Numberaceptará números decimales válidos, mientras que parseIntsimplemente descarta todo más allá del decimal. Por lo tanto, parseIntimita el comportamiento de C, pero quizás no sea ideal para evaluar la entrada del usuario.
  • Ambos recortan espacios en blanco en cadenas.
  • parseInt, al ser un analizador mal diseñado , acepta entrada octal y hexadecimal. Unary plus solo toma hexademical.

Los valores de falsa se convierten a Numberseguir lo que tendría sentido en C: nully falseambos son cero. ""ir a 0 no sigue esta convención pero tiene bastante sentido para mí.

Por lo tanto, creo que si está validando la entrada del usuario, unary plus tiene el comportamiento correcto para todo excepto para aceptar decimales (pero en mi vida real estoy más interesado en capturar la entrada de correo electrónico en lugar de userId, el valor se omite por completo, etc.), mientras que parseInt es demasiado liberal.

djechlin
fuente
2
"Unario plus solo toma hexadecimal" ¿No quieres decir decimal?
krillgar
0

Tenga cuidado, parseInt es más rápido que + operador unario en Node.JS, es falso que + o | 0 son más rápidos, son más rápidos solo para elementos NaN.

Mira esto:

var arg=process.argv[2];

rpt=20000;
mrc=1000;

a=[];
b=1024*1024*1024*1024;
for (var i=0;i<rpt;i++)
 a[i]=Math.floor(Math.random()*b)+' ';

t0=Date.now();
if ((arg==1)||(arg===undefined))
 for (var j=0;j<mrc;j++) for (var i=0;i<rpt;i++) {
  c=a[i]-0;
 }
t1=Date.now();
if ((arg==2)||(arg===undefined)) {
 for (var j=0;j<mrc;j++) for (var i=0;i<rpt;i++) {
  d=a[i]|0;
 }
}
t2=Date.now();
if ((arg==3)||(arg===undefined)) {
 for (var j=0;j<mrc;j++) for (var i=0;i<rpt;i++) {
  e=parseInt(a[i]);
 }
}
t3=Date.now();
 if ((arg==3)||(arg===undefined)) {
 for (var j=0;j<mrc;j++) for (var i=0;i<rpt;i++) {
  f=+a[i];
 }
}
t4=Date.now();

console.log(a[i-1],c,d,e,f);
console.log('Eseguiti: '+rpt*mrc+' cicli');
console.log('parseInt '+(t3-t2));
console.log('|0 '+(t2-t1));
console.log('-0 '+(t1-t0));
console.log('+ '+(t4-t3));
Informate.it
fuente
-3

Considere el rendimiento también. Me sorprendió que parseIntsupere a unary plus en iOS :) Esto es útil solo para aplicaciones web con un gran consumo de CPU. Como regla general, sugeriría que JS opt-guys considere cualquier operador JS sobre otro desde el punto de vista del rendimiento móvil hoy en día.

Entonces, ve primero al móvil ;)

Arman McHitarian
fuente
Como explican las otras publicaciones, hacen cosas muy diferentes, por lo que no puede intercambiar fácilmente una por la otra ...
Bergi
@Bergi, cierto, pero también tienen mucho en común. ¿Dime solo una solución de rendimiento en JavaScript que definitivamente es la única opción correcta? En general, es por eso que la regla general está ahí para nosotros. El resto es específico de la tarea.
Arman McHitarian
3
@ArmanMcHitaryan esta es una microoptimización inútil y no vale la pena. Echa un vistazo a este artículo - fabien.potencier.org/article/8/…
webvitaly
@webvitaly, buen artículo. Siempre hay chicos muy orientados al rendimiento que les gusta escribir el código "lo más rápido posible" y en algunos proyectos específicos eso no está mal. Es por eso que mencioné "JS opt-guys a considerar". esto no es NECESARIO, por supuesto :), pero yo también lo encuentro mucho más legible.
Arman McHitarian
¿Tienes una cita para esto? Tu enlace está roto.
djechlin