ugrás a tartalomhoz

2 + 2 néha 22, avagy összeadás JavaScriptben

Török Gábor · 2010. Szep. 10. (P), 11.29

A Stack Overflown bukkantam a Strangest language feature (röviden: WTF) témára. Érdemes a válaszokat átfutni, számos különc viselkedést vonultatnak fel a válaszadók. Az egyik volt ezek közül a JavaScript (ECMAScript) + operátorának bizonyára már sokatok által megtapasztalt sajátossága.

  1. '2' + 2; // '22'  
  2. '2' - 2; // 0  

A JavaScript nem nyújt külön műveletet stringek összefűzésére, így a + operátor jelenthet string konkatenációt és numerikus értékek összegzését is. Hogy éppen melyik lesz, azt aszerint dönti el, hogy a műveleti jel két oldalán álló összeadandók között van-e string típusú (typeof '2' === 'string'). Ha van, akkor a number típusú összeadandók stringgé lesznek konvertálva (cast). A helyzet akkor érdekesebb, ha egy kifejezésben több összeadandó is szerepel.

  1. 2 + 2 + '2'// 42;  

A + operátor csak két argumentummal apellál, és a kiértékelés balról jobbra történik. Az első műveletben két számot összegzünk, így annak eredménye is numerikus lesz, az azt követő művelet viszont konkatenálás lesz a string típusú összeadandó miatt.

  1. '2' + 2 + 2; // '222'  

A fenti okfejtés illik erre a példára is: az augend string típusú, így az első művelet konkatenáció lesz, aminek string típusú eredményét kapja a második művelet.

A probléma azért életszerű, mert sokszor számok is string típusként érkeznek a függvények bemenetére (XHR válasz, beviteli mező érték sít.).

Jellemzően három bevált gyakorlat áll a rendelkezésre. Talán a leggyakrabban alkalmazott módszer a parseInt() függvény.

  1. parseInt('123', 10); // 123  
  2. parseInt('123a1', 10); // 123  

A parseInt() string típusú értékből integert próbál meg kinyerni. Érdekessége, hogy az első, a megadott számrendszerben nem értelmezett karaktertől levágja a stringet. Csak egész értékek kezeléséhez használható. Párja a parseFloat().

Egy másik járható út a Number objektum függvényként való meghívása, ami az argumentumként kapott string típusú értéket kisérli meg számmá alakítani. Számként nem értelmezhető karakter esetén NaN-nal tér vissza. Érdekessége, hogy üres string esetén 0-t ad vissza.

  1. Number('-12.5'); // -12.5  

Végül talán a legkézenfekvőbb és a Mozilla Developer Center szerzője szerint is javallot út az unáris + operátor használata.

  1. +'-12.5'// -12.5;  
  2. +'2' + 2; // 4  
Unary plus is the fastest and preferred way of converting something into a number, because it does not perform any other operations on the number. It can convert string representations of integers and floats, as well as the non-string values true, false, and null. Integers in both decimal and hexadecimal ("0x"-prefixed) formats are supported. Negative numbers are supported (though not for hex). If it cannot parse a particular value, it will evaluate to NaN.
 
1

utolso megoldast nem

Tyrael · 2010. Szep. 10. (P), 11.34
utolso megoldast nem ismertem, ma is okosabb lettem.

Tyrael
2

Szintén

Kevlar · 2010. Szep. 10. (P), 12.59
Köszönöm a cikket! :)
3

unary jó

amonrpg · 2010. Szep. 10. (P), 16.01
Mielőtt megismertem volna inkább a kivonást alkalmaztam, mint a parseInt-et:
Ugye általában matematikai műveletet végzünk leggyakrabban a számokon, így összekötve a kettőt, egy csapásra átalakította:

var a = '1';
a-1+2 //= 2
4

Mellékhatások

Poetro · 2010. Szep. 10. (P), 17.46
Azért lehetnek ennek mellékhatásai.
  1. x = '0XFA';  
  2. console.log( + x + 3);  
253
és
  1. x = 0XFA;  
  2. console.log( + x + 3);  
253
valamint
  1. x = '0XFA';     
  2. console.log(parseInt(x) + 3);  
253
Ugyanakkor
  1. x = '072';  
  2. console.log( + x + 3);  
75
és
  1. x = '072';  
  2. console.log(Number(x) + 3);  
75
Holott
  1. x = 072;  
  2. console.log( + x + 3);  
61
és
  1. x = '072';   
  2. console.log(parseInt(x) + 3);  
61
Úgyhogy érdemes vigyázni mikor használunk csak + operátort, és mikor parseInt / parseFloat-ot. Amennyiben valaki kíváncsi részletesebben a számokra JavaScriptben, annak ajánlott elolvasni a The Complete Javascript Number Reference-t
5

Kösz

Török Gábor · 2010. Szep. 10. (P), 18.46
Köszönöm a kiegészítést. Ennyire részleteiben nem kívántam taglalni az egyes lehetőségeket, a tízes számrendszerre koncentráltam.
6

Felhasználótól jövő adat

Poetro · 2010. Szep. 10. (P), 19.09
Igen, csak a felhasználótól megkapott adat esetén semmiben nem lehet biztos az ember.
  1. x = '072'// Felhasználótól jövő adat  
  2. if (x > 70) {  
  3.   console.log(+x);               // 72  
  4.   console.log(parseInt(x));      // 58  
  5.   console.log(Number(x));        // 72  
  6.   console.log(parseInt(x, 10));  // 72  
  7. }  
Vagy másik oldalról:
  1. x = '0xFA';                      // Felhasznalótól jövő adat  
  2. y = '70';                        // Pl. szerver oldalról (JSON) string-ként jövő adat  
  3. if (x < y) {  
  4.   console.log(+x);               // 250  
  5.   console.log(parseInt(x));      // 250  
  6.   console.log(Number(x));        // 250  
  7.   console.log(parseInt(x, 10));  // 0  
  8. }  
illetve:
  1. x = '0xFA';                      // Felhasznalótól jövő adat  
  2. y = 70;                          // Pl. szerver oldalról (JSON) számként-ként jövő adat  
  3. if (x > y) {  
  4.   console.log(+x);               // 250  
  5.   console.log(parseInt(x));      // 250  
  6.   console.log(Number(x));        // 250  
  7.   console.log(parseInt(x, 10));  // 0  
  8. }  
Amire ezzel utalni szerettem volna, hogy mindenképpen számot hasonlítsunk össze számmal, azaz előtte alakítsuk át az adatokat számmá, mielőtt műveleteket, illetve összehasonlításokat végzünk rajtuk.
7

exponenciális

Poetro · 2010. Szep. 15. (Sze), 20.58
Álljon még itt egy érdekesség a számokkal kapcsolatban.
  1. var num = '1e8';  
  2. console.log('Number("'+num+'")'': ', Number(num));  
  3. console.log('+"'+num+'"'': ', +num);  
  4. console.log('parseInt("'+num+'")'': ', parseInt(num));  
  5. console.log('parseInt("'+num+'", 10)'': ', parseInt(num, 10));  
  6. console.log('parseInt("'+num+'", 16)'': ', parseInt(num, 16));  
Number("1e8") :  100000000
+"1e8" :  100000000
parseInt("1e8") :  1
parseInt("1e8", 10) :  1
parseInt("1e8", 16) :  488