parseInt vs unary plus, quando usare quale?


149

Quali sono le differenze tra questa linea:

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

e questa linea

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

Questo test jsperf mostra che l'operatore unario è molto più veloce nell'attuale versione di Chrome, supponendo che sia per node.js !?

Se provo a convertire stringhe che non sono numeri, entrambi restituiscono NaN:

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

Quindi, quando dovrei preferire usare parseIntil plus unario (specialmente in node.js) ???

modifica : e qual è la differenza per l'operatore double tilde ~~?


Risposte:


169

Si prega di consultare questa risposta per un set più completo di casi




Bene, ecco alcune differenze che conosco:

  • Una stringa vuota restituisce ""a 0, mentre la parseIntvaluta in NaN. IMO, una stringa vuota dovrebbe essere a NaN.

    +'' === 0;              //true
    isNaN(parseInt('',10)); //true
  • L'unario si +comporta in modo più simile parseFloatpoiché accetta anche i decimali.

    parseIntd'altra parte smette di analizzare quando vede un carattere non numerico, come il punto che deve essere un punto decimale ..

    +'2.3' === 2.3;           //true
    parseInt('2.3',10) === 2; //true
  • parseInte parseFloatanalizza e costruisce la stringa da sinistra a destra . Se visualizzano un carattere non valido, restituisce ciò che è stato analizzato (se presente) come numero e NaNse nessuno è stato analizzato come numero.

    Il unario +d'altra parte restituirà NaNse l'intera stringa non è convertibile in un numero.

    parseInt('2a',10) === 2; //true
    parseFloat('2a') === 2;  //true
    isNan(+'2a');            //true
  • Come visto nel commento di @Alex K. , parseInte parseFloatanalizzerà per carattere. Ciò significa che le notazioni esadecimali ed esponenziali falliranno poiché i xe evengono trattati come componenti non numerici (almeno su base10).

    L'unario +li convertirà correttamente però.

    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.

6
Anche quando si utilizza un radix+"0xf" != parseInt("0xf", 10)
Alex K.

mi piace la tua risposta finora, puoi anche spiegare qual è la differenza per l'operatore double tilde ~~?
hereandnow78,

@ hereandnow78 Questo sarebbe spiegato qui . È equivalente bit a bit di Math.floor(), che sostanzialmente taglia la parte decimale.
Joseph,

4
In realtà, "2e3"non è una rappresentazione intera valida per 2000. È un numero in virgola mobile valido però: parseFloat("2e3")produrrà correttamente 2000come risposta. E "0xf"richiede almeno la base 16, motivo per cui parseInt("0xf", 10)ritorna 0, mentre parseInt("0xf", 16)restituisce il valore di 15 che ti aspettavi.
Bart,

2
@Joseph the Dreamer e @ hereandnow78: La doppia tilde taglia la parte decimale del numero, mentre Math.floor restituisce il numero più basso più vicino. Funzionano allo stesso modo per un numero positivo, ma Math.floor(-3.5) == -4e ~~-3.5 == -3.
Albin,

261

L'ultima tabella di conversione da qualunque numero: Tabella di conversione


2
Si prega di aggiungere "NaN"a questa tabella.
Chharvey,

Potrebbe valere la pena aggiungere una isNaNcolonna a questa tabella: ad esempio, isNaN("")è falso (ovvero è considerato un numero), ma parseFloat("")è NaN, che può essere un gotcha, se stai cercando di utilizzare isNaNper convalidare l'input prima di passarlo aparseFloat
Retsam

È inoltre necessario aggiungere '{valueOf: function(){return 42}, toString: function(){return "56"}}'all'elenco. I risultati contrastanti sono interessanti.
Murrayju,

3
Quindi, il riassunto della tabella è che +è solo un modo più breve di scrivere Number, e quelli più furbi sono solo modi folli per farlo che falliscono nei casi limite?
Mihail Malostanidis,

[] .Undef è una cosa o è solo un modo arbitrario di generare indefinito? Impossibile trovare record di "undef" relativi a JS tramite Google.
jcairney,

10

La tabella nella risposta di thg435 credo sia completa, tuttavia possiamo riassumere con i seguenti schemi:

  • Unary plus non tratta tutti i valori di falsa allo stesso modo, ma tutti ne risultano falsi.
  • Unary plus invia truea 1, ma "true"a NaN.
  • D'altra parte, parseIntè più liberale per le stringhe che non sono cifre pure. parseInt('123abc') === 123, mentre +rapporti NaN.
  • Numberaccetterà numeri decimali validi, mentre parseIntsemplicemente trascina tutto oltre il decimale. Quindi parseIntimita il comportamento di C, ma forse non è l'ideale per valutare l'input dell'utente.
  • Entrambi gli spazi bianchi vengono tagliati nelle stringhe.
  • parseInt, essendo un parser mal progettato , accetta input ottali ed esadecimali. Unary plus richiede solo hexademical.

I valori falsi si convertono nel Numberseguire ciò che avrebbe senso in C: nulle falsesono entrambi zero. ""andare a 0 non segue del tutto questa convenzione, ma per me ha abbastanza senso.

Pertanto penso che se stai convalidando l'input dell'utente, unary plus ha un comportamento corretto per tutto tranne che accetta i decimali (ma nella mia vita reale sono più interessato a catturare l'input e-mail invece di userId, il valore omesso del tutto, ecc.), Mentre parseInt è troppo liberale.


2
"Unary plus richiede solo esadecimali" Non vuoi dire decimale?
Krillgar,

0

Attenzione, parseInt è più veloce di + operatore unario in Node.JS, è falso che + o | 0 sono più veloci, sono più veloci solo per gli elementi NaN.

Controllalo:

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));

-3

Considera anche le prestazioni . Sono stato sorpreso che parseIntbatte unario in più su iOS :) Questo è utile solo per le app Web con un consumo elevato di CPU. Come regola empirica suggerirei ai opt-boys di JS di considerare qualsiasi operatore JS piuttosto che un altro dal punto di vista delle prestazioni mobili al giorno d'oggi.

Quindi, passa al primo cellulare ;)


Come spiegano gli altri post, fanno cose abbastanza diverse, quindi non puoi scambiarne facilmente uno con l'altro ...
Bergi,

@Bergi, giusto, ma hanno anche molto in comune. Dimmi solo una soluzione di prestazioni in JavaScript che è sicuramente l'unica scelta giusta? In generale, è per questo che la regola del pollice è là fuori per noi. Il resto è specifico dell'attività.
Arman McHitarian il

3
@ArmanMcHitaryan questa è inutile microottimizzazione e non ne vale la pena. Dai
webvitaly

@webvitaly, bell'articolo. Ci sono sempre ragazzi molto orientati al perf là ​​fuori che amano solo scrivere il codice "il più veloce possibile" e in alcuni progetti specifici non è male. Ecco perché ho citato "opt-ragazzi di JS da considerare". questo non è assolutamente DEVE ovviamente :), ma lo trovo molto più leggibile in aggiunta.
Arman McHitarian,

Hai una citazione per questo? Il tuo link è rotto.
Djechlin,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.