parseInt vs jednoargumentowy plus, kiedy używać którego?

149

Jakie są różnice między tą linią:

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

i ta linia

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

Ten test jsperf pokazuje, że jednoargumentowy operator jest znacznie szybszy w aktualnej wersji Chrome, zakładając, że jest to dla node.js !?

Jeśli spróbuję przekonwertować ciągi, które nie są liczbami, oba zwracają NaN:

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

Więc kiedy powinienem wolę używać parseIntzamiast jednoargumentowego plusa (szczególnie w node.js) ???

edycja : i jaka jest różnica w stosunku do operatora podwójnej tyldy ~~?

tutaj i teraz 78
źródło
1
Benchmark jsperf.com/parseint-vs-unary-operator
Roko C. Buljan

Odpowiedzi:

169

Zapoznaj się z tą odpowiedzią, aby zapoznać się z pełniejszym zestawem przypadków




Cóż, oto kilka różnic, o których wiem:

  • Pusty ciąg ""szacuje się na a 0, podczas gdy parseIntszacuje go na NaN. IMO, pusty ciąg powinien być NaN.

    +'' === 0;              //true
    isNaN(parseInt('',10)); //true
  • Jednoargumentowy +działa bardziej jak, parseFloatponieważ akceptuje również liczby dziesiętne.

    parseIntz drugiej strony przestaje analizować, gdy widzi znak nienumeryczny, taki jak kropka, która ma być kropką dziesiętną ..

    +'2.3' === 2.3;           //true
    parseInt('2.3',10) === 2; //true
  • parseInti parseFloatanalizuje i buduje ciąg od lewej do prawej . Jeśli zobaczą nieprawidłowy znak, zwraca to, co zostało przeanalizowane (jeśli istnieje) jako liczbę, a NaNjeśli żaden nie został przeanalizowany jako liczba.

    Z +drugiej strony jednoargumentowy zwróci wartość, NaNjeśli cały ciąg nie jest konwertowany na liczbę.

    parseInt('2a',10) === 2; //true
    parseFloat('2a') === 2;  //true
    isNan(+'2a');            //true
  • Jak widać w komentarzu @Alex K. , parseInti parseFloatprzeanalizuje po znaku. Oznacza to, że notacje szesnastkowe i wykładniki nie będą działać, ponieważ xi esą traktowane jako komponenty nienumeryczne (przynajmniej na podstawie 10).

    Jednak jednoargumentowy +konwertuje je poprawnie.

    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.
Józefa
źródło
6
Również przy stosowaniu radixa+"0xf" != parseInt("0xf", 10)
Alex K.
Jak dotąd najbardziej podoba mi się twoja odpowiedź, czy możesz też wyjaśnić, jaka jest różnica w stosunku do operatora podwójnej tyldy ~~?
hereandnow78
@ hereandnow78 To byłoby wyjaśnione tutaj . Jest to odpowiednik bitowy Math.floor(), który w zasadzie odcina część dziesiętną.
Joseph,
4
W rzeczywistości "2e3"nie jest prawidłową reprezentacją liczby całkowitej dla 2000. Jest to jednak poprawna liczba zmiennoprzecinkowa: parseFloat("2e3")poprawnie ustąpi 2000jako odpowiedź. I "0xf"wymaga co najmniej podstawy 16, dlatego parseInt("0xf", 10)zwraca 0, podczas gdy parseInt("0xf", 16)zwraca oczekiwaną wartość 15.
Bart
2
@Joseph the Dreamer i @ hereandnow78: Podwójna tylda odcina dziesiętną część liczby, podczas gdy Math.floor zwraca najbliższą niższą liczbę. Działają tak samo dla liczby dodatniej, ale Math.floor(-3.5) == -4i ~~-3.5 == -3.
Albin
261

Ostateczna tabela konwersji cokolwiek na liczby: Tabela konwersji

Georg
źródło
2
Proszę dodać "NaN"do tej tabeli.
chharvey
Może warto dodać isNaNkolumnę do tej tabeli: na przykład isNaN("")jest fałszywa (czyli jest uważana za liczbę), ale parseFloat("")jest NaN, co może być problemem, jeśli próbujesz użyć isNaNdo walidacji danych wejściowych przed przekazaniem ich doparseFloat
Retsam
Powinieneś również dodać '{valueOf: function(){return 42}, toString: function(){return "56"}}'do listy. Mieszane wyniki są interesujące.
murrayju
3
Podsumowując tabelę, jest +to po prostu krótszy sposób pisania Number, a te bardziej szalone to po prostu szalone sposoby, które zawodzą w skrajnych przypadkach?
Mihail Malostanidis
Czy [] .undef jest rzeczą, czy jest to tylko arbitralny sposób generowania undefined? Nie można znaleźć żadnego wpisu „undef” związanego z JS przez Google.
jcairney,
10

Uważam, że tabela w odpowiedzi thg435 jest wyczerpująca, jednak możemy podsumować następujące wzorce:

  • Unary plus nie traktuje wszystkich fałszywych wartości tak samo, ale wszystkie są fałszywe.
  • Jednoargumentowy plus wysyła truedo 1, ale "true"do NaN.
  • Z drugiej strony parseIntjest bardziej liberalny dla łańcuchów, które nie są czystymi cyframi. parseInt('123abc') === 123, mając na uwadze, że +raporty NaN.
  • Numberzaakceptuje prawidłowe liczby dziesiętne, a parseIntpo prostu zrzuci wszystko poza przecinek. W ten sposób parseIntnaśladuje zachowanie języka C, ale być może nie jest idealny do oceny danych wejściowych użytkownika.
  • Oba przycinają białe znaki w łańcuchach.
  • parseIntbędąc źle zaprojektowanym parserem , akceptuje dane wejściowe ósemkowe i szesnastkowe. Jednoargumentowy plus przyjmuje tylko szesnastkowe.

Fałszywe wartości są konwertowane na Numbernastępujące, co miałoby sens w C: nulli falseobie wynoszą zero. ""przejście do 0 nie do końca jest zgodne z tą konwencją, ale ma dla mnie wystarczająco dużo sensu.

Dlatego myślę, że jeśli sprawdzasz dane wejściowe użytkownika, unary plus ma poprawne zachowanie dla wszystkiego, z wyjątkiem tego, że akceptuje liczby dziesiętne (ale w moich prawdziwych przypadkach bardziej interesuje mnie przechwytywanie danych wejściowych e-mail zamiast identyfikatora użytkownika, wartość całkowicie pominięta itp.), Podczas gdy parseInt jest zbyt liberalne.

djechlin
źródło
2
„Jednoargumentowy plus przyjmuje tylko szesnastkowe” Nie masz na myśli liczby dziesiętnej?
krillgar
0

Uważaj, parseInt jest szybsze niż operator + jednoargumentowy w Node.JS, nieprawdą jest, że + lub | 0 są szybsze, są szybsze tylko dla elementów NaN.

Sprawdź to:

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
źródło
-3

Weź pod uwagę również wydajność . Zaskoczyło mnie, że parseIntna iOS bije unary plus :) Jest to przydatne tylko w przypadku aplikacji internetowych z dużym obciążeniem procesora. Zgodnie z ogólną zasadą sugerowałbym opt-facetom JS, aby rozważył dowolnego operatora JS zamiast innego z punktu widzenia wydajności mobilnej w dzisiejszych czasach.

Więc idź najpierw na urządzenia mobilne ;)

Arman McHitarian
źródło
Jak wyjaśniają inne posty, robią zupełnie inne rzeczy, więc nie można łatwo zamienić jednego na drugie…
Bergi
@Bergi, racja, ale mają też wiele wspólnego. Powiedz mi tylko jedno rozwiązanie wydajnościowe w JavaScript, które jest zdecydowanie jedynym właściwym wyborem? Generalnie dlatego są dla nas praktyczne zasady. Reszta zależy od zadania.
Arman McHitarian
3
@ArmanMcHitaryan to bezużyteczna mikrooptymalizacja i nie warto. Sprawdź ten artykuł - fabien.potencier.org/article/8/ ...
webvitaly
@webvitaly, fajny artykuł. Zawsze są ludzie zorientowani na perfekcję, którzy po prostu lubią pisać „najszybszy możliwy” kod, aw niektórych konkretnych projektach nie jest to złe. Dlatego wspomniałem o „opt-faceci z JS do rozważenia”. to oczywiście NIE MUSI :), ale ja sam uważam to za dużo bardziej czytelne.
Arman McHitarian,
Czy masz na to cytat? Twój link jest uszkodzony.
djechlin