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.

'2' + 2; // '22'
'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.

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.

'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.

parseInt('123', 10); // 123
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.

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.

+'-12.5'; // -12.5;
+'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.
x = '0XFA';
console.log( + x + 3);
253
és
x = 0XFA;
console.log( + x + 3);
253
valamint
x = '0XFA';   
console.log(parseInt(x) + 3);
253
Ugyanakkor
x = '072';
console.log( + x + 3);
75
és
x = '072';
console.log(Number(x) + 3);
75
Holott
x = 072;
console.log( + x + 3);
61
és
x = '072'; 
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.

x = '072'; // Felhasználótól jövő adat
if (x > 70) {
  console.log(+x);               // 72
  console.log(parseInt(x));      // 58
  console.log(Number(x));        // 72
  console.log(parseInt(x, 10));  // 72
}
Vagy másik oldalról:
x = '0xFA';                      // Felhasznalótól jövő adat
y = '70';                        // Pl. szerver oldalról (JSON) string-ként jövő adat
if (x < y) {
  console.log(+x);               // 250
  console.log(parseInt(x));      // 250
  console.log(Number(x));        // 250
  console.log(parseInt(x, 10));  // 0
}
illetve:
x = '0xFA';                      // Felhasznalótól jövő adat
y = 70;                          // Pl. szerver oldalról (JSON) számként-ként jövő adat
if (x > y) {
  console.log(+x);               // 250
  console.log(parseInt(x));      // 250
  console.log(Number(x));        // 250
  console.log(parseInt(x, 10));  // 0
}
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.
var num = '1e8';
console.log('Number("'+num+'")', ': ', Number(num));
console.log('+"'+num+'"', ': ', +num);
console.log('parseInt("'+num+'")', ': ', parseInt(num));
console.log('parseInt("'+num+'", 10)', ': ', parseInt(num, 10));
console.log('parseInt("'+num+'", 16)', ': ', parseInt(num, 16));
Number("1e8") :  100000000
+"1e8" :  100000000
parseInt("1e8") :  1
parseInt("1e8", 10) :  1
parseInt("1e8", 16) :  488