Vissza az előzőleg látogatott oldalra (nem elérhető funkció)Vissza a tananyag kezdőlapjára (P)Ugrás a tananyag előző oldalára (E)Ugrás a tananyag következő oldalára (V)Fogalom megjelenítés (nem elérhető funkció)Fogalmak listája (nem elérhető funkció)Oldal nyomtatása (nem elérhető funkció)Oldaltérkép megtekintése (D)Keresés az oldalon (nem elérhető funkció)Súgó megtekintése (S)

Modern programozási minták a kliens és szerveroldali webprogramozásban / A JavaScript nyelvi elemei

Tanulási útmutató

Összefoglalás

A fejezet a JavaScript nyelvi elemeit mutatja be részletesen. Az összetettebb elemek, mint például az objektumok, a következő fejezetben kerülnek részletesebb kifejtésre.

A JavaScript nyelvi elemei

A JavaScript a jól ismert C vagy még inkább Java programozási nyelvhez hasonló típusokkal, utasításokkal és vezérlési szerkezetekkel rendelkezik, legalábbis első látásra. Jobban odafigyelve markáns különbségekbe ütközhetünk, melyeket jó dolog észben tartani. A JavaScript lazán típusos, prototípus-orientált, függvény-scope-okat használ, ráadásul úton-útfélen függvénykifejezéseket, lambda függvényeket írunk. Az lehet az első benyomásunk, hogy izgalmas és gyors a JavaScriptben való fejlesztés, azonban ne feledjük, hogy a túlságosan megengedő, a szabályokat lazán kezelő nyelv könnyedén megbosszulhatja magát. Bizonyos kódmennyiség után — ha nem figyelünk oda, akkor — kezelhetetlen, olvashatatlan vagy memóriaszivárgásokkal teli programkódot kaphatunk!

A programozásban a nyelvi elemek alapja valahol a nyelv által nyújtott típusoknál kezdődik. Mielőtt azonban erről beszélnénk fontos egy kicsit kibontani mit is jelentenek az adatok. Minden adat valahol egy literálforma környékén reprezentálódik először, amely egy típus leírására szolgál, a típus pedig valamilyen primitív értéket, vagy azok halmazát fogja jelenteni. Akkor nézzük is meg részletesebben, hogy hogyan épülnek fel ezek az adatok!

Literálnak nevezzük azt a szimbólumot amivel egy értéket vagy típust megjelenítünk a kódban, így pl. az 1 egy szám-, az 'asdfgf' pedig egy sztring literál. Ezeket általában egyszerű értékek definiálására használjuk, azonban a JavaScript lehetőséget ad olyan komplexebb adatok megjelenítésére is, mint amilyenek az objektumok vagy a függvények. A most következő értékek és adattípusok legtöbbjét a literál-formájukon keresztül fogjuk megismerni.

Értékek

A JavaScript nyelv legegyszerűbb alkotóelemei az úgynevezett primitív értékek. Minden érték jelentéssel bír, ami a kontextustól függően akár más és más is lehet: például minden értéknek van logikai jelentése, így könnyedén felhasználhatjuk őket egy elágazás feltételében, vagy például bármelyik érték egy neki megfelelő sztringgé konvertálható. A primitívek senkit se tévesszenek meg, nem összekeverendők a típusokkal, melyek mindegyike valamilyen módon egy értéket wrap-pel, vagyis ölel körbe.

Bár az ECMAScript öt különböző értéket specifikál, azonban a futtatókörnyezetek általában ide sorolják az undefined-ot is:

Sztring: string

A sztringek jelentése egyszerűen egy karakter, pl. 'a' vagy 'karakterek sokasága'. A JavaScript a sztring literál leírásakor nem tesz különbséget a ' és a " jelek között, nincs sztring interpoláció, így bármelyiket bárhol használhatjuk, ahol szövegeket definiálnánk.

Megjegyzés

sztring interpoláció: változók behelyettesítése sztringbe. Pl. PHP-ban elfogadott megoldás az "Ez egy $targy", ahol a $targy változó értéke behelyettesítődik a szövegbe.

Szám: numeric

A JavaScript csak lebegőpontos (float) számokat ismer: pl. 3 vagy 3.33. A numeric az egyértelmű szám-értékeken kívül három kiemelt konstans-értékkel is rendelkezik:

Bár más reprezentációban, 10-estől eltérő számrendszerben is létrehozhatunk számokat, a háttérben azok azonnal numeric-re konvertálódnak:

Forráskód
3           // => 3
-3.33       // => -3.33
.33         // => rövidített alakban is írható a numeric, 0.33
0777        // => oktális, vagyis 8-as számrendszerbeli szám,
            // értéke 511 lesz az automatikus konverzió miatt
0xFFFFFF    // => 16-os számrendszerbeli szám, értéke az 16777215
            // numeric lesz

Vigyázzunk, strict módban már nem használhatjuk az oktális formát!

Mivel a JavaScript minden számot és matematikai műveletet átalakít a belső numeric reprezentációjára és az ezzel végezhető számításokra, kézenfekvő, hogy eleve így használjuk mindenhol. Sok kellemetlenségtől óvhatjuk meg magunkat, ha kerüljük a különböző 10-estől eltérő számrendszerek és bináris műveletek használatát!

A JavaScript az IEEE 754 lebegőpontos számábrázolást használja, ami rendelkezik egynéhány hátulütővel. Mivel ez a bináris reprezentáció nem tudja pontosan kezelni a tizedestörteket, így pl. a 0.1 + 0.2 nem lesz egyenlő a 0.3-al, kerekítési hiba miatt az összeadás értéke 0.30000000000000004 lesz! Bár a legtöbb programozási nyelv float adattípusa rendelkezik ezzel a problémával, a JavaScript sajnos nem ad másik típust, hogy azt használjuk a pontos aritmetikára — így ezzel a tulajdonsággal mindig számolnunk kell, ha nagy pontosságú matematikai műveleteket végzünk! A probléma legkézenfekvőbb megoldása, ha a nem egész műveleteket normalizáljuk:

Forráskód
var a = 0.1,
    b = 0.2,
    normalizaltOsszeadas = (a*10 + b*10)/10;
assertEquals(0.3, normalizaltOsszeadas);

Logikai érték: boolean

Két logikai értéket ismerünk, a true-t (igazat) és a false-t (hamisat).

A JavaScriptben minden primitívnek, típusnak, műveletnek van egy logikai értéke, melyet logikai kontextusban (pl. egyenlőségvizsgálatban) használhatunk fel. Ez a logikai érték általában igaz, kivéve az alábbi hat esetben, melyek mindig false-t jelentenek:

A fenti eseteket szoktuk falsy (hamiskás) értékeknek is nevezni. Ezt a hat esetet kivéve minden érték logikai kontextusban igaznak számít, így pl. a "false" vagy a "0" sztring is, illetve bármilyen objektum, tömb, stb. még akkor is, ha üres!

Egyéb értékek

Ha egy változót definiálunk, de explicit értéket még nem adtunk neki, a JavaScript automatikusan az undefined értékkel látja el. Ugyanezt kapjuk, ha nem adunk saját visszatérési értéket egy függvénynek vagy metódusnak.

Ha valahol — pl. egy objektum adattagjának alapértékeként — egy meghatározott értéktípust várnánk, azonban egyetlen ilyen sem áll rendelkezésre, akkor sokszor null, vagyis az üres értéket érdemes beállítani. Bár a JavaScript néhány beépített metódusa is null-al tér vissza, a tiszta és átlátható kód érdekében szorítkozzunk inkább arra, hogy csak értékadásként, esetleg értéktörlésre használjuk.

A JavaScript lehetőséget ad arra, hogy reguláris kifejezéseket egy egyszerű, /kifejezés/ formátumú alakban is megadjunk. Ezeket a többi értékhez hasonlóan használhatjuk fel, így például tárolhatjuk változóban, átadhatjuk paraméterként vagy lehet egy függvény visszatérési értéke.

Vissza a tartalomjegyzékhez

Típusok

A JavaScript típusai egy primitív értéket vagy primitív értékek valamilyen kombinációját tartalmazzák. A következőkben röviden áttekintjük a nyelv típusait, a megadásukhoz szükséges literálokat és egymáshoz való kapcsolatukat.

Típus lekérdezése a typeof operátorral

A typeof operátor bármilyen kifejezés, pl. egy változó típusát adja vissza sztringként:

Forráskód
var szam = 1,
    sztring = 'asdfgf';
assertEquals('number', typeof szam);
assertEquals('string', typeof sztring);

Vigyázzunk a typeof használatával, ugyanis csak a következő típusokat azonosítja: undefined, object, boolean, number, string, function és xml — minden egyéb kifejezésre object-et fog visszaadni!

Automatikus típuskonverzió

A JavaScript automatikus típuskonverziót hajt végre minden olyan esetben, ha két különböző típuson próbálunk valamilyen közös műveletet végrehajtani (pl. egy számot összeadni egy sztringgel).

Forráskód
assertEquals(1, 2 / "2");
assertEquals(4, "2" * 2);
assertEquals("11", 1 + "1");
assertEquals(2, 1 + true);
assertEquals(0, null + false);
assertEquals("It is true", "It is " + true);
assertEquals(true, "3" < 4);

Bár az automatikus típuskonverzió jó ötletnek tűnhet bizonyos esetekben, mégis jobb, ha elkerüljük és a kezünkben tartjuk az értékek konverzióját. A motiváció, hogy így járjunk el nem más, mint az olvashatóság és az egyértelműség. Az automatikus konverzió a környező típusok és az operátorok által meghatározott sorrendben történik, melyet folyamatosan fejben kell tartani, ha ráhagyatkozunk.

Az automatikus típuskonverziót egyszerűen elkerülhetjük úgy, hogy időben megbizonyosodunk egy kifejezés típusáról a (pl. typeof kulcsszó segítségével) és megtagadjuk a műveletet vagy átkonvertáljuk a problémás változót.

Fontos megjegyezni, hogy az automatikus típuskonverziónak van egy széles körben elfogadott módja: tetszőleges kifejezés logikai kontextusban való felhasználása. Ez annak köszönhető, hogy minden értéknek egyértelmű logikai jelentése van és false értéket csak akkor vehet fel, ha a korábban említett hat érték valamelyikéről van szó.

Szám: number

A JavaScriptben minden szám number típusú és numeric értékű lesz, azonban definiálásuk sokféleképpen történhet.

Integer jellegű értéket egyszerű tízes vagy tizenhatos (hexadecimális) literállal adhatunk meg:

Forráskód
var integer = 2,
    negativInteger = -2,
    hexadecimalisInteger = 0x000FFF;
// minden numerikus értéket automatikusan
// decimális értékké konvertál:
assertEquals(4095, hexadecimalisInteger);

Lebegőpontos literállal valós számokat is megjeleníthetünk, illetve rövidíthetünk az Integer-ek definícióján. Leírásuk sok más nyelvhez hasonlóan a decimális pont illetve opcionálisan az e (vagy E) betű után megadott karakterisztikával történik:

Forráskód
var valosSzam = 0.32,
    valosSzamRovid = .32,
    negativValos = -0.32,
    negativValosRovid = -.32,
    integerKarakterisztikaval = -2.32e5,
    valosBonyolult = -.32e-2;
// -2.32e5 = -2.32 * (10^5), tehát:
assertEquals(-232000, integerKarakterisztikaval);
assertEquals(valosSzam, valosSzamRovid);
assertEquals(negativValos, negativValosRovid);
// -.32e-2 = -0.32 * (10^-2), tehát:
assertEquals(-0.0032, valosBonyolult);

Tartózkodjunk a fenti példa valosBonyolult kifejezéséhez hasonló definícióktól, már a literálok definiálásánál is törekedjünk az egyszerűségre és olvashatóságra!

Természetesen a konstans numeric értékeket is leírhatjuk egy szám-literállal, melyeket az előbbiekhez hasonlóan number-ként fogja azonosítani a typeof operátor:

Forráskód
assertEquals('number', typeof Infinity);
assertEquals('number', typeof -Infinity);
assertEquals('number', typeof NaN);

Mivel a NaN is szám, így sosem bízhatunk meg igazán a typeof által visszaadott értékben. Szerencsénkre a JavaScript biztosít egy beépített isNaN függvényt, amellyel le tudjuk ellenőrizni, hogy egy number típusú kifejezés valójában NaN-e.

Forráskód
assertEquals(false, isNaN(1));
assertEquals(true, isNaN(NaN));

Jó tudni, hogy az isNaN nem ellenőriz típust, így ha egy függvénnyel azt ellenőriznénk, hogy a paraméterként kapott érték number típusú-e, akkor szükség lesz a typeof használatára is:

Forráskód
function isNumber(number) {
    return (typeof number === 'number') && !isNaN(number);
}
assertEquals(true, isNumber(1));
assertEquals(true, isNumber(2.32));
assertEquals(false, isNumber(NaN));
assertEquals(false, isNumber(2/"a"));
Megjegyzés

Ha a végtelen értékeket is ki szeretnénk zárni, akkor a typeof típusellenőrzés helyett használhatjuk az isFinite függvényt a véges, number típusú értékek vizsgálatára!

A number-ek operátorai a megszokottak, használatukkor megpróbálják mindkét operandust számmá konvertálni a művelet előtt, amennyiben valamelyik nem volt az. Egyetlen kivétel ez alól a + operátor, ami megegyezik a sztring konkatenációval, melynek az összeadásnál erősebb precendenciája van, így a szám operandust fogja sztringgé konvertálni!

Forráskód
assertEquals(true, 5 < 6);
assertEquals(false, 5 > 6);
assertEquals(true, 5 === (4+1));
assertEquals(6, 2 * 3);
assertEquals(2, 6 / 3);
// Maradékos osztás:
assertEquals(1, 5 % 2);
// Automatikus típuskonverzió sztringre:
assertEquals("11", 1 + "1");
assertEquals("11", "1" + 1);

Gyakran előforduló eset, hogy sztringeket szeretnénk számmá konvertálni, ezt könnyedén megtehetjük a parseInt vagy a parseFloat függvényekkel. Előbbi metódusnak opcionálisan átadható az átalakításhoz használt számrendszer is.

Forráskód
assertEquals(755, parseInt("755"));
assertEquals(7, parseInt("7.55"));
assertEquals(493, parseInt("755", 8));
assertEquals(755, parseInt("755px"));
assertEquals(NaN, parseInt("Px is 755"));
assertEquals(755, parseFloat("755"));
assertEquals(7.55, parseFloat("7.55"));
assertEquals(2, parseInt("1") + 1);
Megjegyzés

A parseInt metódus az ES5-öt nem támogató böngészőkben meglepetést okozhat azzal, hogy automatikusan detektálni próbálja a paraméterként kapott sztring számrendszerét. Ha legacy klienseket is támogatni szeretnénk, akkor mindig használjunk ES5-Shim-et vagy explicit adjuk át a 10-et a második paraméterben, így biztosan decimális számmá konvertáljuk.

Létezik egy Number nevű beépített objektum a JavaScriptben, mely igen hasznos, számokkal kapcsolatos metódusokat ad számunkra, bár közvetlen használata nem ajánlott — ugyanis, ha példányosítunk egy Number-t, akkor annak típusa nem number hanem object lesz, értékét pedig csak a valueOf metódussal tudjuk lekérdezni. Ilyen módon egyértelmű, hogy két Number összeadása sem egyszerű feladat. Ha nem így, hát használjuk máshogy, hiszen így is óriási haszna van: a JavaScriptben minden nem-objektum típuson meghívhatjuk a neki megfelelő beépített objektum metódusait. Ilyen esetben a függvény futási idejére a literált átmenetileg „körbeöleli” ez az objektum és úgy viselkedik, mintha ő maga is egy object lenne. Amint a metódus lefut, visszatér a neki megfelelő értékkel, de a literált, amint meg lett hívva, változatlanul hagyja.

Az előbbi következményeként nem meglepő, hogy a nem-objektum típusok értéke megváltoztathatlan (immutable).

Forráskód
var szam = 12.98;
assertEquals("12.98", szam.toString());
// A toPrecision az adott hosszhoz igazítja a számot:
assertEquals("12.980", szam.toPrecision(5));
assertEquals("13.0", szam.toPrecision(3));
assertEquals("13", szam.toPrecision(2));
// A toFixed adott tizedesjegyre kerekíti a számot:
assertEquals("12.98000", szam.toFixed(5));
assertEquals("12.98", szam.toFixed(2));
assertEquals("13.0", szam.toFixed(1));
// A műveletek során nem változott meg a szam értéke:
assertEquals(12.98, szam);

A JavaScript biztosít egy statikus Math nevű objektumot néhány gyakori aritmetikai művelethez:

Forráskód
assertEquals(3.141592653589793, Math.PI);
assertEquals(5, Math.abs(-5));
assertEquals(3, Math.floor(3.5));
assertEquals(4, Math.ceil(3.5));
// A random 0 és 1 közötti véletlenszámot generál:
Math.random();
// Kis trükkel generálhatunk más számok között is
// Pl. 1 és 10 között:
Math.ceil(Math.random()*10);

Sztring: string

Hosszabb vagy rövidebb, akár egy karakter hosszú szöveges adatot sztring literállal hozhatunk legkönnyebben létre. Minden sztring típus egy string primitív értéket fog hordozni. A sztringek egyetlen operátora a konkatenáció (+).

Forráskód
var sztring1 = 'asdfgf',
    sztring2 = "asdfgf",
    karakter = 'a',
    escapeeltSztring = 'JavaScript\'s string';
assertEquals(true, sztring1 === sztring2);
assertEquals('string', typeof sztring1);
assertEquals('string', typeof karakter);
assertEquals('asdfgfasdfgf', sztring1 + sztring2);

A számokhoz hasonlóan a sztring literál is egy immutable sztringet fog létrehozni, a hozzá tartozó String objektum pedig ugyanúgy körbeöleli, míg egy metódusát meghívjuk rajta.

Forráskód
assertEquals('asdfgf', ' asdfgf '.trim());
assertEquals('ASDFGF', 'asdfgf'.toUpperCase());
assertEquals(5, 'asdfgf'.length);

Érdemes végignézni a String metódusait, hiszen rengeteg szövegekkel kapcsolatos műveletet biztosít a számunkra, felesleges lenne újra megírni ezeket.

Forráskód
// A String.replace-el a sztringben tudunk karaktersorokat
// más karaktersorokra cserélni:
assertEquals('Snickersnee', 'Snickersknee'.replace('knee', 'nee'));
// A String.search-el egy sztringben kereshetünk,
// a találat indexét adja vissza, ha pedig nem találja
// a keresett szubsztringet, akkor "-1"-et
assertEquals(10, 'Lickety-split'.search('li'));
assertEquals(-1, 'Lickety-split'.search('ili'));
// A String.split egy sztringet a megadott karaktersor
// mentén szétdarabol és a darabokat egy tömbben adja vissza:
assertEquals(['Ding', 'Ding', 'Dong'], 'Ding,Ding,Dong'.split(','));

Sajnos a JavaScript nem engedi többsoros sztringek írását, így, ha egy hosszabb szöveget szeretnénk több darabra tördelni, akkor kénytelenek vagyunk azt konkatenációval megtenni:

Forráskód
var html = "<section>" +
                "<h1>Hosszabb szöveg</h1>" +
           "</section>";
Megjegyzés

A konkatenáció alternatívájaként a sorvégi (láthatatlan) sortörés jeleket is kiescape-elhetjük, azonban ez a módszer azonnal csődöt mond, ha véletlenül egy whitespace karakter marad a \ jel után.

A legtöbb objektumnak van toString metódusa (még a Number-nek is, így minden számon meghívható!), mellyel sztringgé tudjuk konvertálni az értékét. Azonban ez nem mindig megfelelő — például a null és az undefined értékek esetén — ekkor használhatjuk a String metódust önmagába. Előbbi valójában a String objektum konstruktor-függvénye, melynek példányosítása a Number-nél már leírt okokból nem ajánlott, de konvertálásra, egyszerű függvényhívással biztonságosan használható.

Forráskód
assertEquals('true', String(true));
assertEquals('5', String(5));
assertEquals('/regexp/i', String(/regexp/i));

Logikai érték: boolean

A logikai típust egy true vagy egy false logikai literállal hozhatunk létre. A típus operátorai a más nyelveknél megszokottak: ! (tagadás), && (és), || (vagy).

A korábbiakhoz hasonlóan a Boolean a típushoz tartozó objektum, amely túl sok hasznos metódust nem tartogat a számunkra, példányosításától pedig ezúttal is tartózkodjunk. A legtöbb esetben elegendő, ha egy kifejezés implicit logikai jelentésére hagyatkozunk, azonban a sztringeknél már látott módon használhatjuk a Boolean konstruktor-függvényt bármely kifejezés explicit logikai értékké való konvertálására. Egy másik megoldás ugyanerre, ha a kifejezést kétszer letagadjuk a ! operátorral (mivel a ! logikai operátor, így a kifejezést logikai értékké konvertálja a futtatókörnyezet, viszont ekkor az eredeti jelentésének tagadása lesz a kapott érték, így újra le kell tagadnunk).

Forráskód
var truthyNumber = 1,
    falsyNumber = 0,
    truthyString = 'string',
    falsyString = '';
assertEquals(true, Boolean(truthyNumber));
assertEquals(true, Boolean(truthyString));
assertEquals(false, Boolean(falsyNumber));
assertEquals(false, Boolean(falsyString));
assertEquals(true, !!truthyNumber);
assertEquals(true, !!truthyString);
assertEquals(false, !!falsyNumber);
assertEquals(false, !!falsyString!);

A JavaScriptben az és és a vagy operátorokat gyakran használjuk logikai feltételeken kívül is: az && az úgynevezett guard operátor, míg a || a default.

Guard operátor:

Ha több és operátort láncolunk egymás után, akkor ez a lánc addig a pontig fog kiértékelődni, amíg az éppen vizsgált kifejezés igazat ad. Az utolsó kifejezés akkor fog kiértékelődni, ha minden korábbi igaz lett. Ezt a tulajdonságot jól ki tudjuk használni arra, hogy megvizsgáljuk, hogy egy objektum rendelkezik-e egy metódussal és csak akkor futtassuk ha minden rendben. Az &&-t ekkor guard operátornak nevezzük.

Forráskód
var book = {
        getISBN: function() 
    };
// A book egy objektum, ami logikai kontextusban igaz, hiszen
// nincs a falsy értékek között, ugyanez igaz a book getISBN metódusára
if (book && book.getISBN) {
    book.getISBN();
}
// Mivel nincs fly metódusa egy könyvnek, így azt sosem fogja meghívni
book && book.fly && book.fly();

Mivel a JavaScript csak lazán típusos és bármilyen típus átadható bárhol, így egy metódus futtatása esetén bizonyos esetekben nem árt megbizonyosodni arról, hogy a futtatandó metódus valóban létezik-e. Szerencsére, ahogy a fenti példában is látható, ez a művelet nem kerül nagy erőfeszítésbe.

Default operátor:

Az egymás után láncolt vagy operátorok úgynevezett default operátorként funkcionálnak. Mivel a || az első olyan értékkel tér vissza, ami igaz, így tökéletes választás alapértékek beállítására. Ha egyetlen feltétel sem lett igaz, akkor a művelet az utolsó kifejezés értékével tér vissza.

Forráskód
var book = function(name, datas) {
    // A név vagy a megadott név volt vagy egy üres sztring lesz
    name = name || '';
    datas = datas || ;
    datas.isbn = datas.isbn || '0-00-000000-0';
    datas.pages = datas.pages || 0;
    datas.author = datas.author || 'Author not provided';
    datas.name = name;
    return datas;
};
var bookDatas = book('Hally Pottel', {
        isbn: 0,
        pages: 50,
        name: 'Something else'
    });
// Szerző nem volt megoldva, így az default érték lesz:
assertEquals('Author not provided', datas.author);
// A lapok száma meg volt adva, így az nem a default érték:
assertEquals(50, datas.pages);
// Vigyázzunk a default operátorral, ugyanis a falsy értékeket
// nem veszi figyelembe, ahogy az átadott 0-át az isbn-nél:
assertEquals('0-00-000000-0', datas.isbn);
assertEquals('Hally Pottel', datas.name);

Gyakori a komplexebb kifejezések használata, mikor is a függvények visszatérési értékét vizsgáljuk. Az előbbi példához közel maradva megpróbálhatjuk könyvesboltok adatait két különböző módon is lekérdezni, és csak abban az esetben példányosítani egyet, ha a korábbi módszerek nem vezettek eredményre:

Forráskód
var getBookstore = function(bookstoreName) {
    return getLocalBookStore(bookstoreName) ||
           getGlobalBookStore(bookstoreName) ||
           new BookStore({ name: bookstoreName });
};

Reguláris kifejezések

A reguláris kifejezések olyan minták, melyek sztringek bizonyos részeire illeszkednek. Segítségükkel különböző keresési és cserélési műveleteket egyszerűsíthetünk le egyetlen parancsba. Természetesen utóbbiak feltétele, hogy az adott feladat megoldható legyen a reguláris kifejezések eszközkészletével, mintaillesztési technikával.

A reguláris kifejezés a modern webfejlesztés egyik sarokkövének tekinthető. Igaz, hogy bármi megoldható nélküle, azonban rengeteg problémára olyan elegáns és rövid megoldást kínál, hogy vétek mellőzni a használatát. A JavaScriptben lépten-nyomon találkozhatunk vele, pl. HTML tag-ek, CSS selector-ok vagy értékek feldolgozásakor, nagyobb szövegek értelmezésénél és konvertálásakor, adatok előkészítésénél és így tovább.

JavaScriptben egy ilyen kifejezést legkönnyebben a megfelelő literálformával hozhatunk létre, egyszerűen a / jelek használatával:

Forráskód
// Illeszkedik az "a string" sztringre:
var regexp = /a string/;

Bár korábban már többször említettük, hogy kerülendőek a típusokhoz tartozó beépített objektumok, azonban ha a reguláris kifejezés létrehozását dinamikusan szeretnénk megtenni, változóból emelnénk be egy részét, akkor csak a RegExp konstruktorfüggvény segítségével tehetjük meg:

Forráskód
var suffix = "string";
var regexp = new RegExp('a ' + suffix);

Reguláris kifejezések írása

A legegyszerűbb minta az egyszerű karaktersor, mint pl. a /string/, mely megadott karakterekre a leírt sorrendben illeszkedik. Ezt könnyedén tesztelhetjük a RegExp test metódusával:

Forráskód
var regexp = /string is found/;
assertEquals(true, regexp.test('True when string is found'));
assertEquals(false, regexp.test('False when string is not found'));

Speciális karakterekkel fejezhetünk ki különböző megszorításokat, vagy ismétlődéseket a kódban. A . például a sortörésen kívül bármilyen karakterre igaz, a ? azt jelenti, hogy az előtte álló karakter maximum egyszer, a * azt, hogy akárhányszor szerepelhet, a + pedig azt, hogy legalább egyszer meg kell jelennie:

Forráskód
assertEquals(true, /True.*found/.test('True when string is found'));
assertEquals(true, /A.C/.test('ABC'));
assertEquals(false, /A.C/.test('ABBC'));
assertEquals(true, /A.?C/.test('AC'));
assertEquals(true, /A.?C/.test('ABC'));
assertEquals(false, /A.?C/.test('ABBC'));
assertEquals(true, /A.C?/.test('AB'));
assertEquals(true, /A.+C/.test('AFC'));
assertEquals(true, /AB+C/.test('ABC'));
assertEquals(true, /AB+C/.test('ABBC'));
assertEquals(false, /AB+C/.test('AC'));

Lehetőségünk van explicit módon megmondani, hogy hányszor fordulhat elő egy kiválasztott karakter vagy karaktersor — az {n} azt jelenti, hogy n darab, az {n,m} pedig azt, hogy n és m közötti előfordulás engedélyezett:

Forráskód
assertEquals(true,  /AB{3}C/.test('ABBBC'));
assertEquals(false, /AB{3}C/.test('ABBC'));
assertEquals(true,  /AB{3,4}C/.test('ABBBC'));
assertEquals(true,  /AB{3,4}C/.test('ABBBBC'));
assertEquals(false, /AB{3,4}C/.test('ABBBBBC'));

A \ jellel védhetünk le egy karaktert, ha az a reguláris kifejezések nyelvi elemei közé tartozik:

Forráskód
assertEquals(true, /A\.C/.test('A.C'));
assertEquals(false, /A\.C/.test('ABC'));

Kapcsos zárójelek között felsorolhatjuk az adott helyen engedélyezett karaktereket. Itt akár intervallumot is megadhatunk, pl. [0-9] az összes szám az [a-z] pedig az összes kisbetű. Természetesen összetetettebb karakterlista is megadható, a [0-9a-z_] például az összes számra, kisbetűre és az alulvonásra is igaz lesz. Ha éppen a definiált karakterek ellentétjére szűrnénk, akkor a felsorolás negálható a lista elejére írt ^ jellel:

Forráskód
assertEquals(true,  /A[FG]C/.test('AFC'));
assertEquals(false, /A[FG]C/.test('ABC'));
assertEquals(true,  /A[0-9]C/.test('A2C'));
assertEquals(true,  /A[0-9]?C/.test('AC'));
assertEquals(true,  /A[0-9]?C/.test('A1C'));
assertEquals(true,  /A[0-9]*C/.test('A1234C'));
assertEquals(false, /A[0-9]*C/.test('AB1C'));
assertEquals(true,  /A[^A-Z]C/.test('A1C'));
assertEquals(false, /A[^A-Z]C/.test('ABC'));

A | jel a logikai „vagy”-ot jelenti, a minta a bal és a jobb operandusa is igaz lehet az adott helyen:

Forráskód
assertEquals(true, /A|B/.test('A'));
assertEquals(true, /A|B/.test('B'));
assertEquals(false, /A|B/.test('C'));

A ^ jel a szöveg elejét, a $ pedig a végét jelöli:

Forráskód
assertEquals(true, /3$/.test('123'));
assertEquals(true, /^A/.test('ABB'));
assertEquals(true, /^A.*Z$/.test('ABBGFSZ'));
assertEquals(true, /^A.*Z$/.test('ABBGFSZ'));
assertEquals(true, /string is found$/.test('True when string is found'));
assertEquals(false, /^string is found$/.test('True when string is found'));
assertEquals(true, /^String is found$/.test('String is found'));
assertEquals(false, /^String is found$/.test('String is found!'));

A fentiek tükrében már rengeteg dolgot megtehetünk: nézzünk is példát egy tipikus felhasználási területre, a felhasználótól jött adatok validálására:

Forráskód
// Magyar irányítószám ellenőrzése, 1. helyen
// 1-9-es számok, a 2-4. helyen pedig 0-9-ig
// kerülhetnek számok:
var isValidZip = function(zip) {
    return /^[1-9][0-9]{3}$/.test(zip);
};
assertEquals(true, isValidZip('1234'));
assertEquals(false, isValidZip('0234'));
assertEquals(false, isValidZip('12345'));
assertEquals(false, isValidZip('1a34'));

A JavaScript reguláris kifejezéseiben szigorúan meg vannak különböztetve a kis- és a nagybetűk. Ha ezt a tulajdonságot meg szeretnénk szüntetni, akkor írjuk a kifejezés mögé az i kapcsolót:

Forráskód
var regexp = /aABb/i;
assertEquals(true, regexp.test(aabb));

A RegExp-ek a mintaillesztést az első illeszkedésnél befejezik, azonban a g kapcsolóval globálissá tehetjük az ellenőrzést. Ha sztringekben szeretnénk cseréket végezni, akkor érdemes ezt észben tartani és bekapcsolni.

A reguláris kifejezések eszköztárának egyik leghasznosabb művelete a csoportosítás / kiválasztás. Sima zárójelek között olyan karaktersorozatot adhatunk meg, amelyeket egy csoportban szeretnénk kezelni. A zárójelezett kifejezések mellékhatása, hogy egy úgynevezett backreference-t hoz létre, vagyis a kiválasztott karakterekre később visszahivatkozhatunk a \x jellel (ahol x a kiválasztás sorszámát jelenti):

Forráskód
// A sztring közepén levő BA karaktereknek pontosan
// háromszor kell ismétlődniük:
assertEquals(true,  /A(BA){3}C/.test('ABABABAC'));
// A backreference-ekkel lehetővé válik, hogy
// egy kiválasztott karaktersorra visszahivatkozzunk:
assertEquals(true, /<(.+)>(.+)<\/\1>/.test('<strong>whatever</strong>'));
// A sztring közepén páros számú ismétlődésnek
// kell lennie:
assertEquals(true,  /A(.*)\1C/.test('ABABAC'));
assertEquals(true,  /A(.*)\1C/.test('AFFC'));
assertEquals(false, /A(.*)\1C/.test('AFC'));
// Komplex kifejezés két megjegyzett
// kataktersorozattal:
assertEquals(true,  /(.)(.+)\1C\2/.test('AFFACFF'));
assertEquals(false, /(.)(.+)\1C\2/.test('AFFCFF'));

Sajnos a csoportosítás művelete a reguláris kifejezésekben mindig együtt jár a kiválasztással is, így megnehezíthetjük a dolgunkat bizonyos esetekben, mikor utóbbi tulajdonságra nem lenne szükség. Ahhoz, hogy úgy tudjunk karaktersorokat vagy más kifejezéseket csoportosítani, hogy a kiválasztás ne történjen meg, három operátort kapunk: az egyszerű ellenőrzést, az előretekintő és a negált előretekintő ellenőrzéseket.

Egyszerű ellenőrzéshez használjuk a (?:x) formátumú operátort (ahol x egy tetszőleges kifejezés). Ez a művelet csak összevon karaktereket, egy csoportban kezel egy kifejezést, de nem alkalmazza a kiválasztást rajta, vagyis nem hoz létre backreference-et.

Forráskód
assertEquals(true, /A(?:BA)/.test('ABA'));
assertEquals(true, /A(?:BA){2}(.+)\1$/.test('ABABAFCFC'));

Előretekintő ellenőrzésnek nevezzük a x(?=y) formátumú műveletet, mellyel megmondhatjuk, hogy egy karaktersor után pontosan milyen kifejezést várunk el. Egyszerűen szólva a mintaillesztés csak akkor lesz igaz, ha x után mindenképp az y kifejezés szerepel. Különlegessége abban áll, hogy a (?=y) rész a mintaillesztésben nem fog szerepelni, ez csak egy egyszerű feltétel x-re! Az egyszerű ellenőrzéshez hasonlóan az előretekintő ellenőrzés sem hoz létre backreference-t.

Forráskód
assertEquals(true,  /Hally(?=Pottel|Blown)/.test('HallyPottel'));
assertEquals(false, /Hally(?=Pottel|Blown)/.test('HallyPotter'));
// A következő mintában a (.*) rész a Hally és az & jel
// közötti teljes karaktersorra illeszkedik, hiszen az
// előretekintő ellenőrzés csak a Hally-re vonatkozó
// tulajdonság, másra nem használható a mintaillesztés során:
assertEquals(true,  /Hally(?=Pottel)(.*)&\1/.test('HallyPottels&Pottels'));
assertEquals(false, /Hally(?=Pottel)(.*)&\1/.test('HallyPottels&s'));

Negált előretekintő ellenőrzésnek a x(?!y) formátumú műveletet nevezzük. Pontosan ugyanúgy működik mint az előretekintő ellenőrzés, csak éppen azt ellenőrizzük, hogy x után semmiképp sem szerepelhet y. Természetesen az előbbihez hasonlóan, az ellenőrzés csak az x-re vonatkozik, nem számít a mintaillesztés egyéb műveleteiben, illetve itt sem jön létre backreference.

Forráskód
assertEquals(false, /Hally(?!Pottel|Blown)/.test('HallyPottel'));
assertEquals(true,  /Hally(?!Pottel|Blown)/.test('HallyPotter'));

A JavaScript reguláris kifejezés-világa rövidítéseket is biztosít néhány gyakran használt karakter-halmazra:

Reguláris kifejezések használata

A reguláris kifejezések test metódusát az előző példákban már sokat használtuk, így részletesebben már nem térnénk ki rá. Ha arra van szükségünk, hogy egy sztringre ellenőrizzük, hogy igaz-e rá a reguláris kifejezés, akkor ez a legegyszerűbb megoldás.

Legtöbbször a sztringek reguláris kifejezéseket is elfogadó metódusainál találkozunk RegEx-ekkel, hiszen néhány műveletet sokkal hatékonyabbá tehetünk a segítségükkel. Egyszerű példa erre, ha egy szöveg bizonyos részeit szeretnénk lecserélni. Az ominózus részeket néha nagyon bonyolult lenne kiválasztani a hagyományos, iteratív módszerekkel — legtöbbször azonban egyszerűen és tömören kiszelektálhatjuk egy reguláris kifejezéssel. Szerencsére a String.replace parancs elfogad RegEx-et is paramétereként:

Forráskód
assertEquals('John Wayne', 'John Smith'.replace(/\w*$/, 'Wayne'));
// Alakítsuk át a CSS attribútumok JavaScriptesített neveit
// eredeti formájukba, ahogy egy CSS tagbe illeszthetőek:
assertEquals("font-family", "fontFamily".replace(/([A-Z])/g, "-$1").toLowerCase());
// A replace második paraméterében $1, $2, stb. néven
// hivatkozhatunk a backreference-ekre:
assertEquals('Smith John', 'John Smith'.replace(/(\w+)\s(\w+)$/, '$2 $1'));

A String.match egy tömbben adja vissza a szöveg azon részeit, melyre illeszkedik a minta:

Forráskód
// Találjuk meg azokat a számokat amelyek egymás
// után többször szerepelnek:
assertEquals(['22', '444'], '06301223444'.match(/(\d)\1+/gi));

A match segítségével persze jóval bonyolultabb is megoldhatóak, például kinyerhetjük egy CSS szabályból az összes benne szereplő attribútum nevét:

Forráskód
var css = "" +
"   margin: 0 10px;\n" +
"   padding: 0 5px;\n" +
"   background-color: red;\n" +
"   display: inline-block;";
var expectedResult = ["margin", "padding", "background-color", "display"];
// A minta a következő felépítést jelenti:
// Válassz ki minden olyan sztringet, amiben legalább egy alfa-
// numerikus karakter vagy - jel van és utána közvetlenül egy :
// található. Az előretekintő ellenőrzés miatt nem szerepel a
// kiválasztott értékekben a pontosvessző!
var matchRegex = /[\w-]+(?=:)/gi;
assertEquals(expectedResult, matchRegex);

A String.match-hez hasonló a split metódus is, melynél azonban nem közvetlenül szelektálhatunk ki elemeket a bementből, hanem az elválasztást definiálhatjuk a reguláris kifejezéssel.

Forráskód
var countPartsOfPhoneNumber(number) {
    return number.split(/[ ()-]+/gi).length;
}
assertEquals(4, countPartsOfPhoneNumber('+36 (30) 481-9873'));

Objektumok és tömbök

A JavaScript nyelv referenciatípusait a tömbök és az objektumok képviselik a nyelvben. Ezekről a következő fejezetben lesz szó.

Vissza a tartalomjegyzékhez

Vezérlési szerkezetek

JavaScript vezérlési szerkezeteinek áttekintése

Flash lejátszó letöltése

JavaScript vezérlési szerkezetek

Vissza a tartalomjegyzékhez

Új Széchenyi terv
A projekt az Európai Unió támogatásával, az Európai Szociális Alap társfinanszirozásával valósul meg.

A tananyag az ELTE - PPKE informatika tananyagfejlesztési projekt (TÁMOP-4.1.2.A/1-11/1-2011-0052) keretében valósult meg.
A tananyag elkészítéséhez az ELTESCORM keretrendszert használtuk.