parseInt vs unary plus, quando usar qual?

149

Quais são as diferenças entre esta linha:

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

e esta linha

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

Este teste jsperf mostra que o operador unário é muito mais rápido na versão atual do chrome, assumindo que é para o node.js.?

Se eu tentar converter strings que não são números, ambos retornam NaN:

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

Então, quando devo preferir usar parseInto plus unário (especialmente no node.js) ???

edit : e qual a diferença para o operador double til ~~?

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

Respostas:

169

Por favor, veja esta resposta para um conjunto mais completo de casos




Bem, aqui estão algumas diferenças que eu conheço:

  • Uma cadeia vazia é ""avaliada como a 0, enquanto é parseIntavaliada como NaN. IMO, uma string em branco deve ser a NaN.

    +'' === 0;              //true
    isNaN(parseInt('',10)); //true
  • O unário +age mais como parseFloatuma vez que também aceita decimais.

    parseIntpor outro lado, para de analisar quando vê um caractere não numérico, como o período que se destina a ser um ponto decimal ..

    +'2.3' === 2.3;           //true
    parseInt('2.3',10) === 2; //true
  • parseInte parseFloatanalisa e constrói a string da esquerda para a direita . Se eles virem um caractere inválido, ele retornará o que foi analisado (se houver) como um número e NaNse nenhum foi analisado como um número.

    O unário, +por outro lado, retornará NaNse a sequência inteira não for convertível em um número.

    parseInt('2a',10) === 2; //true
    parseFloat('2a') === 2;  //true
    isNan(+'2a');            //true
  • Como visto no comentário de @Alex K. , parseInte parseFloatanalisará por caractere. Este hex meios e notações expoente falhará desde o xe esão tratados como componentes não numéricos (pelo menos em Base10).

    O unário +irá convertê-los corretamente embora.

    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
fonte
6
Além disso, quando usando uma raiz+"0xf" != parseInt("0xf", 10)
Alex K.
Eu gosto da sua resposta até agora, você também pode explicar qual é a diferença para o operador double til ~~?
hereandnow78
@ hereandnow78 Isso seria explicado aqui . É equivalente a bit a bit Math.floor(), que basicamente corta a parte decimal.
11743 Joseph
4
Na verdade, "2e3"não é uma representação inteira válida para 2000. No entanto, é um número de ponto flutuante válido: parseFloat("2e3")será exibido corretamente 2000como resposta. E "0xf"requer pelo menos a base 16, e é por isso que parseInt("0xf", 10)retorna 0, enquanto parseInt("0xf", 16)retorna o valor de 15 que você estava esperando.
Bart
2
@ Joseph the Dreamer e @ hereandnow78: O til duplo corta a parte decimal do número, enquanto Math.floor retorna o número mais baixo mais próximo. Eles funcionam da mesma forma para número positivo, mas Math.floor(-3.5) == -4e ~~-3.5 == -3.
Albin
261

A melhor tabela de conversão para qualquer número: Tabela de conversão

georg
fonte
2
Por favor, adicione "NaN"a esta tabela.
chharvey
Pode valer a pena adicionar uma isNaNcoluna a esta tabela: por exemplo, isNaN("")é false (ou seja, é considerado um número), mas parseFloat("")é NaN, o que pode ser uma pegadinha, se você estiver tentando usar isNaNpara validar a entrada antes de passá-la paraparseFloat
Retsam
Você também deve adicionar '{valueOf: function(){return 42}, toString: function(){return "56"}}'à lista. Os resultados mistos são interessantes.
murrayju
3
Então, o resumo da tabela é que +é apenas uma maneira mais curta de escrever Number, e as mais furtivas são apenas maneiras loucas de fazer que falham em casos extremos?
Mihail Malostanidis
[] .Undef é uma coisa ou isso é apenas uma maneira arbitrária de gerar indefinido? Não foi possível encontrar nenhum registro de "undef" relacionado ao JS através do Google.
jcairney
10

A tabela na resposta de thg435 que acredito ser abrangente, no entanto, podemos resumir com os seguintes padrões:

  • O Unary plus não trata todos os valores falsos da mesma forma, mas todos saem falsificados.
  • Unary plus envia truepara 1, mas "true"para NaN.
  • Por outro lado, parseInté mais liberal para strings que não são dígitos puros. parseInt('123abc') === 123, enquanto que +relatórios NaN.
  • Numberaceitará números decimais válidos, enquanto parseIntapenas ignora tudo além do decimal. Assim, parseIntimita o comportamento C, mas talvez não seja ideal para avaliar a entrada do usuário.
  • Ambos cortam o espaço em branco nas cadeias.
  • parseInt, sendo um analisador mal projetado , aceita entrada octal e hexadecimal. Unary plus leva apenas hexadecimal.

Os valores de falsidade se convertem em Numberseguir o que faria sentido em C: nulle falsesão zero. ""ir para 0 não segue essa convenção, mas faz bastante sentido para mim.

Portanto, acho que se você está validando a entrada do usuário, o unary plus tem um comportamento correto para tudo, exceto que ele aceita decimais (mas, nos meus casos da vida real, estou mais interessado em capturar entrada de email em vez de userId, valor omitido inteiramente etc.), enquanto parseInt é muito liberal.

djechlin
fonte
2
"Unary plus leva apenas hexadecimal" Você não quer dizer decimal?
precisa saber é o seguinte
0

Cuidado, parseInt é mais rápido que o operador unário no Node.JS, é falso que + ou | 0 sejam mais rápidos, eles são mais rápidos apenas para elementos NaN.

Veja isso:

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
fonte
-3

Considere o desempenho também. Fiquei surpreso que parseIntsupere o unário e mais no iOS :) Isso é útil para aplicativos da Web com alto consumo de CPU. Como regra geral, sugiro que os JS opt-guys considerem qualquer operador JS em detrimento de outro do ponto de vista do desempenho móvel atualmente.

Então, vá para o celular primeiro ;)

Arman McHitarian
fonte
Como os outros posts explicam, eles fazem coisas bem diferentes, então você não pode trocar facilmente um pelo outro ...
Bergi
@ Bergi, certo, mas eles também têm muito em comum. Diga-me apenas uma solução de desempenho em JavaScript que é definitivamente a única escolha certa? Em geral, é por isso que a regra de ouro está à nossa disposição. O resto é específico da tarefa.
Arman McHitarian
3
@ArmanMcHitaryan, essa microoptimização é inútil e não vale a pena. Confira este artigo - fabien.potencier.org/article/8/…
webvitaly
@webvitaly, bom artigo. Sempre existem pessoas muito orientadas para o desempenho por aí que simplesmente gostam de escrever o código "o mais rápido possível" e em alguns projetos específicos isso não é ruim. Por isso mencionei "JS opt-guys a considerar". isso não é OBRIGATÓRIO, é claro :), mas também acho muito mais legível.
Arman McHitarian
Você tem uma citação para isso? Seu link está quebrado.
djechlin