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 / Függvények és a futási környezet

Tanulási útmutató

Összefoglalás

A JavaScript nyelv másik lényeges eleme a nyelv funkcionális aspektusát biztosító függvények. E fejezet a függvények viselkedését mutatja be mélyreható módon.

Függvények és a futási környezet

A függvény egy olyan programrész, mely egy külön alprogramnak tekinthető és általában a program más részei indítják el, vagyis hívják meg. A szoftver egyéb részeihez hasonlóan a függvény is műveletek meghatározott sorrendjét tartalmazza, melyet a függvénytörzsnek nevezünk. Minden függvénynek átadhatóak értékek, melyet paramétereknek vagy argumentumoknak nevezünk és ő maga is visszaadhat egyet, amely a visszatérési érték lesz.

A JavaScript függvények különlegessége, hogy úgynevezett first-class tulajdonsággal rendelkeznek, más szóval:

A következőkben a fenti tulajdonságok mentén ismerkedünk meg a JavaScript függvényeinek világával, funkcionális természetével és azzal a különleges kódolási stílussal, melyet mindez lehetővé tesz.

Függvények létrehozása

A JavaScriptben egy függvény valójában egy Function objektum, mely a többi objektumtól csak néhány dologban tér el — a legfontosabb különbség azonban, hogy meghívható.

Minden függvény rendelkezik visszatérési értékkel, melyet a return kulcsszó után adhatunk meg. Amennyiben explicit nem definiálunk egyet, a függvény akkor is visszaad egy undefined-ot, vagyis mindig van valamilyen visszatérési érték!

A JavaScript függvényeinek tetszőleges számú paraméter átadható, melyeket a paraméterlistában adhatunk meg. Nem szükséges minden argumentumot kitölteni a függvény meghívásakor, ekkor a szabadon hagyott paraméterek a függvényen belül undefined értékűek lesznek.

Forráskód
function hero(name, title) {
    return name + ' is not ' + title;
}
assertEquals('Superman is not undefined', hero('Superman'));

Függvényt négy különböző módon hozhatunk létre JavaScriptben.

Függvény létrehozása deklarációval

A függvények létrehozásának egyik hagyományos módja a deklaráció:

Forráskód
function name([param[, param[, ... param]]]) {
   statements
}

A függvény nevének megadása ilyen esetben kötelező, később ezzel a névvel hivatkozhatunk rá.

A függvénydeklaráció eredményeként egy olyan függvényt kapunk, ami az egész futtatási kontextusban elérhető:

Forráskód
// Valójában már itt elérhető a getHero függvény
var hero = getHero();
assertEquals('Superman', hero);
function getHero() {
    return 'Superman';
};

Függvény létrehozása függvénykifejezésként

A függvények first-class tulajdonságuk miatt bárhol, bármikor létrehozhatóak kifejezés formában. Ekkor egy objektum-referenciaként jelennek meg, melyet tetszőlegesen eltárolhatunk vagy tovább adhatunk. A függvénykifejezést a deklarációhoz hasonlóan hozhatjuk létre, azonban ekkor a név paraméter is opcionális:

Forráskód
function [name]([param] [, param] [..., param]) {
   statements
}

Ha nem adunk meg egy függvénykifejezésnek, akkor úgynevezett Anonim függvényt hozunk létre.

Forráskód
// Anonym függvény átadása egy változónak
var getHero = function() {
    return 'Superman';
};
assertEquals('Superman', getHero());
// Függvénykifejezést bárhol használhatunk, ahol egyébként
// objektumokat használhatunk, így pl. lehet visszatérési
// érték is:
var greetingFactory = function() {
    return function(hero) {
        return "Hi, I'm " + hero + "!";
    };
};
var greeting = greetingFactory();
assertEquals("Hi, I'm Superman!", greeting('Superman'));

Ha egy függvénykifejezést egy objektum property-jéhez rendeljük, akkor az a függvény az objektum metódusa lesz — függvénytörzsében pedig a this kulcsszóval utalhatunk az objektumra:

Forráskód
var greeting = {
        fromSuperman: function() {
            return this.say('Superman');
        },
        say: function(hero) {
            return "Hi, I'm " + hero + "!";
        }
    };
assertEquals("Hi, I'm Superman!", greeting.fromSuperman());

Igaz, hogy legtöbb esetben név nélkül hozunk létre függvénykifejezéseket, azonban bizonyos esetekben segíthet ha mégis elnevezzük őket. Elsősorban akkor láthatjuk ennek hasznát, hogyha a programunk a futása során valahol hibába fut. Ebben az esetben a stacktrace-ben végigkövethetjük a függvény stack-et — vagyis a listát az éppen hívás alatt lévő függvényekről — azonban az anonim függvények megnehezíthetik ennek áttekintését, hiszen itt is névtelenek lesznek.

Első látásra furcsa lehet, hogy a JavaScriptben a függvény is egy objektum, hogy egy egyszerű referenciaként elmenthetjük egy változóba — azonban érdemes ezzel a tulajdonsággal minél inkább megbarátkozni, hiszen szerves részét képezi a nyelv ökoszisztémájának:

Forráskód
var greetings = [
        function() { return 'Hey boy, welcome!'; },
        function() { return "Hi, I'm a superhero!" }
    ],
    superman = {
        greet: greetings[1]
    };
assertEquals("Hi, I'm a superhero!", greetings[1]());
assertEquals("Hi, I'm a superhero!", superman.greet());

Függvény létrehozása a nyíl operátorral

Az EcmaScript 6 egy egyszerűsített szintaktikát is biztosít a függvénykifejezés létrehozására. Az úgynevezett Fat Arrow (=>) operátor segítségével kihagyhatjuk a function kulcsszót a kifejezésből. Az így létrehozott függvények mindig anonim függvények lesznek!

Egysoros metódusok esetén lehetőségünk van elhagyni a kapcsos zárójeleket — illetve ebben az esetben a nyíl operátor utáni kifejezés automatikusan implicit visszatérési érték is lesz, azaz nem szükséges kiírni a return kulcsszót.

Forráskód
([param[, param[, ... param]]]) => {
   statements
}
param => expression

Amennyiben nincs paraméter akkor a nyíl operátor elé mindenképp ki kell írni egy üres paraméterlistát, egy paraméternél viszont elhagyhatóak a zárójelek!

Forráskód
var getHero = () => 'Superman',
    showHero = (hero, address) => {
        return hero + ' from ' + address;
    };
assertEquals('Superman', getHero());
assertEquals('Superman from Kripton', showHero('Superman', 'Kripton'));

Első ránézésre talán kicsit furcsa és idegen lehet a Fat arrow használata, azonban nem is a fenti példák tükrözik az elsődleges felhasználási területet. A kompakt szintaxis akkor válik expresszívvé, ha függvényparaméterek helyén használjuk:

Forráskód
var heroes = [
        'Superman',
        'Spiderman',
        'Sugarman'
    ];
// Ha össze szeretnénk számolni a hősök neveinek hosszát, akkor
// legegyszerűbb a map függvényt használni:
heroes.map(function(hero) { return hero.length; });
// A Fat arrow operátorral lényegesen expresszívebb kódot kapunk:
heroes.map( hero => hero.length );

Függvény létrehozása a Function objektummal

Ha a függvények létrehozásáról beszélünk, akkor meg kell említeni, hogy a többi típushoz hasonlóan, a függvény is létrehozható a neki megfelelő, Function nevű beépített objektummal. Ekkor lehetőségünk van sztringként átadni a függvénytörzset — így akár dinamikusan, futási időben generálhatjuk a függvény magját.

A Function objektum konstruktorként való alkalmazása ezúttal sem ajánlott, hiszen rengeteg szoftver-karbantarthatósági problémát okozhat, nehezíti a kód olvashatóságát és a hibakeresést, ráadásul a böngészők nem tudják megfelelően optimalizálni az így létrehozott programrészt.

Változók kontextusa

Amíg a legtöbb nyelvben egy-egy változó csak a deklarálás programblokkjában érhető el — ezt szokás blokk scope-nak nevezni — addig a JavaScriptben a változók hatóköre az a teljes függvény, amiben létrehoztuk őket — azaz e nyelv esetén függvény-scope-ról beszélünk.

Bárhol is definiáljuk a változót, az a függvény teljes törzsében elérhető lesz, azonban az értékadás pillanatáig undefined értékű.

Forráskód
function scopeTest() {
    // Ezen a ponton a,b,c,i változók mind
    // léteznek, de értékük undefined
    var a = 1;
    // Itt is létezik az összes változó, de
    // "a" értéke mostantól 1
    for (var i=0; i<5; i++) {
        // Itt már i-nek is van értéke
        var b = 2;
    }
    // Ezen a ponton:
    // a: 1, b: 2, i: 5, c: undefined
    if (b === 1) {
        var c = 3;
    }
    // Minden változó értéke megmaradt, mivel
    // a feltételbe nem ment be a futtatókörnyezet,
    // így c továbbra is létezik, értéke undefined
    console.log(c); // => undefined
    //
    console.log(d); // => ReferenceError: d is not defined
}

Mivel a JavaScriptben egy függvényen belül definiálhatunk egy másik, belső függvényt (és abban továbbiakat), így egy külső függvényben deklarált változót a belső függvényekben is el tudunk érni. Ez a működés meglepő lehet más nyelvekből érkezők számára, azonban egyértelmű, hiszen a belső függvény a külső scope-jában van. Természetesen a belső függvényekben egy hasonló nevű változóval elfedhetjük a kívül létrehozott változót, ebben az esetben úgy módosíthatjuk annak értékét, hogy a változtatások csak a belső függvényt érintik.

Forráskód
var outerFunction = function() {
    var a = 1,
        b = 2,
        c = 3,
        innerFunction = function(b) {
            var c = 4,
                d = 5;
            a = 2;
            // Ezen a ponton a globális "b" és "c" változó
            // el van fedve, így "b" undefined, a "c" lokális
            // változó értéke pedig 4. A "d" egy új lokális
            // változó, a függvényen kívül nincs definiálva
        }
        // Ezen a ponton a,b,c értéke a scope-nak
        // megfelelően 1,2,3 és d változó nem létezik
        innerFunction();
        // A függvény lefutása után az "a" értéke módosul
        // 2-re, hiszen az innerFunction nem fedi el,
        // sőt megváltoztatja azt!
};

A JavaScriptben a globális névtér egy, a szkript betöldősékor automatikusan lefutó függvénynek is tekinthető, így az itt létrehozott változók elérhetőek a teljes programkódban.

Forráskód
var heroOuter = 'Spiderman',
function showMeHero() {
    var heroInner = 'Superman';
    return heroInner + ' is not ' + heroOuter;
};
assertEquals('Spiderman', heroOuter);
assertEquals('Superman is not Spiderman', showMeHero());
assertEquals(undefined, heroInner);

Figyeljünk oda azonban arra, hogy ne szennyezzük a globális névteret, hiszen a könnyelműen deklarált függvények és változók felülírhatják vagy összeakadhatnak mások által írt kódokkal, komponensekkel. A JavaScriptben egy bevett módszer, hogy a globális névtérhez egyetlen objektumon keresztül csatlakozik az alkalmazásunk, mintegy névtérként használjuk azt. A globális névtér használatának elkerülésére népszerű minta az AMD modul, megfelelő használatával elérhetjük, hogy a globális névtérben (a beépítetteken túl) csupán a modulkezelő objektumai legyenek, minden más el van fedve.

Önkioldó függvény

Mivel a JavaScriptben a függvény is kifejezés, így lehetőségünk van a definiálás helyén azonnal futtatni is. Jogos lehet a kérdés, hogy miért is lenne erre szükség — ha úgy is azonnal lefut a függvény, akkor mi értelme egy újabb programblokkal bonyolítani az életünket? A kérdésre az előző fejezet ad választ: mivel a JavaScript függvény scope-ot használ, szükség van valamilyen megoldásra ahhoz, hogy elszeparálhassuk a privát, csak az aktuális kódhoz tartozó változókat a globális névtértől, illetve a programkomponenseket egymástól. Az önkioldó függvényekkel a JavaScriptben natívan nem létező névtereket emulálhatjuk a legegyszerűbb módon:

Forráskód
(function() {
    var hero = 'Superman',
        greet = function() {
            return "Hi I'm " + hero;
        };
    greet();
}());
// Itt, az önkioldó függvényen kívül nem érhető el a hero
// változó és a greet metódus.

Függvényargumentumok

A függvények értékként kapják meg az argumentumaikat, így ha egy paramétert a függvényen belül módosítunk az nem fog kifelé megváltozni. Természetesen kivételt képeznek az objektum típusok, hiszen ezekre mindig referenciaként hivatkozunk, így is adódnak át — ha megváltoztatjuk őket egy függvénytörzsben, akkor azt az objektumot módosítjuk, amire a referencia mutat.

Forráskód
function showHero(hero, address) {
    return hero + ' from ' + address;
}
var hero = 'Superman',
    address = 'Kripton';
assertEquals('Superman from Kripton', showHero(hero, address));
// Ha megváltoztatjuk a függvenyen belül az értékeket,
// azok nem változnak meg kifelé:
function showHero(hero, address) {
    hero = 'Spiderman';
    address = 'Earth';
    return hero + ' from ' + address;
}
var hero = 'Superman',
    address = 'Kripton';
assertEquals('Spiderman from Earth', showHero(hero, address));
assertEquals('Superman', hero);
assertEquals('Kripton', address);
// Ha objektumok a paraméterek, akkor azok referenciaként adódtak át,
// vagyis az objektum maga módosítható, a módosítás ugyanazon az
// objektumon történik, mint amire a függvényen kívül is hivatkozunk,
// így természetesen a változás kifelé is megtörténik:
function showHero(hero, address) {
    hero.name = 'Spiderman';
    return hero.name + ' from ' + address.planet;
}
var hero = {
        name: 'Superman'
    },
    address = {
        planet: 'Kripton'
    };
assertEquals('Spiderman from Earth', showHero(hero, address));
assertEquals('Spiderman', hero.name);
assertEquals('Kripton', address.planet);
// Ha egy paraméterben egy objektum-referencia van, és a paramétert
// átírjuk, akkor a függvényen kívüli, ugyanerre az objektumra mutató
// változók természetesen nem fognak módosulni:
function showHero(hero, address) {
    hero.name = 'Sugarman';
    address = {
        planet: 'Mars'
    };
    return hero.name + ' from ' + address.planet;
}
var hero = {
        name: 'Superman'
    },
    address = {
        planet: 'Kripton'
    };
assertEquals('Sugarman from Mars', showHero(hero, address));
assertEquals('Sugarman', hero.name);
assertEquals('Kripton', address.planet);

Az EcmaScript 6 a függvényparaméterek tulajdonságait is hasznos lehetőségekkel bővíti. Bevezetésre került – a más nyelvekben igen népszerű — alapértékek fogalma, mellyel egyes argumentumok értékét állíthatjuk be arra az esetre, ha a függvény meghívásánál nincs átadva neki megfelelő érték.

Forráskód
var showHero = function(hero = 'Superman', address = { planet: 'Kripton'}) {
    return hero + ' from ' + address.planet;
}
assertEquals('Superman from Kripton', showHero());
// Explicit undefined átadása esetén a default paraméter
// jut szerephez:
assertEquals('Superman from Kripton', showHero(undefined));

Az ES6 alapértelmezett paraméter-értéke ráadásul megengedi, hogy bármilyen kifejezést megadhassunk neki, így akár egy függvényhívás eredménye is lehet argumentum. További hasznos tulajdonsága, hogy az alapérték futási időben értékelődik ki, tehát az így átadott metódus csak akkor hívódik meg, ha tényleg szükség van rá!

Forráskód
var getDefaultHero = function() {
        return 'Superman';
    },
    showHero = function(hero = getDefaultHero(), address = "Kripton") {
        return hero + ' from ' + address;
    };
assertEquals('Superman from Kripton', showHero());

EcmaScript 6 kompatibilis futtatókörnyezetben lehetőségünk van változó hosszúságó paraméterlisták kezelésére is, az úgynevezett rest parameter használatával. A ...param definiálása esetén a param változó egy tömb lesz, mely a függvény nem nevesített paramétereit tartalmazza.

Forráskód
// A datas tömb a második paramétertől fogva tartalmazza az összes
// argumentumot:
var showHero = function(name, ...datas) {
    return name + ' has the following superpowers: ' + datas.join(', ');
};
assertEquals(
    'Sugarman has the following superpowers: fat, hungry',
    showHero('Sugarman', 'fat', 'hungry')
);
Megjegyzés

Az ES6-ot még nem támogató futtatókörnyezetekben is lehetőség van a változó hosszúságú paraméterlisták kezelésére, a függvényekben elérhető arguments változó segítségével. Sajnos azonban az arguments nem teljes értékű tömb és nem foglalkozik a nevesített paraméterekkel sem. Az ES5-ben deprecated, az ES6-ból pedig már teljesen kivették ezt az objektumot, így használatát érdemes mellőzni az időtálló kód érdekében!

Callback függvények

Callback-nek nevezzük azokat a függvényeket, amelyeket egy későbbi hívás kedvéért eltárolunk — e hívás pedig akkor történik meg, ha a callback számára megfelelő utasítássor lefutott. Tipikusan aszinkron hívások állapotváltozásaira kötjük őket, illetve különböző widget-ek, komponensek beállításainál találkozhatunk velük, hiszen segítségükkel a magunk számára bővíthetjük azok funkcionalitását.

Mivel a JavaScriptben nagyon egyszerű anonim függvényeket létrehozni, eltárolni vagy éppen átadni, így a callback-ek írása a nyelv egy meghatározó eleme, nagyon sok helyen találkozhatunk vele.

Nézzünk egy egyszerű példát egy callback függvényre:

Forráskód
var showHero = function(name, callback) {
        console.log("I'm " + name);
        if (callback) {
            callback();
        }
    };
showHero('Superman', function() {
    console.log('Callback invoked!');
});
// => "I'm Superman"
// => "Callback invoked!"

A callback függvényt egy paraméterként adtuk át a showHero számára, melyet akkor hív meg, miután kiírta a hős nevét a konzolra.

Ahogy a példában is látszik, érdemes a callback függvényeket opcionálisként kezelni, hiszen egyáltalán nem biztos, hogy valaki használni is szeretné őket. Természetesen az ezért felelős feltétel lecserélhető egy elegáns guard operátorra: callback && callback().

Ha a callback függvénynek valamilyen — az adott helyzetben hasznos — paramétert adunk át, akkor bővebb funkcionalitást biztosítunk a külvilág számára.

Forráskód
// A tömbök map metódusa az összes elemen végigfut, mindre
// meghívva az átadott callback-et, melynek visszatérési
// értékeiből pedig egy új tömböt épít:
var numArray = [1,2,3].map(function(el) {
      return el * 2;
    });
assertEquals([2, 4, 6], numArray);
// A node.js fs.readfile metódusa egy fájlt olvas be
// aszinkron módon, vagyis a függvény visszatérési
// értéke nem tartalmazza a fájl tartalmát - az csak
// az IO művelet végén lesz elérhető. Ha ez megtörtént,
// akkor meghívódik az átadott callback metódus,
// argumentumában pedig megkapja a fájl tartalmát:
fs.readFile('somefile.txt', function (err, data) {
  // Hiba dobása ha nem sikerült a beolvasás
  if (err) throw err;
  console.log(data); // A fájl tartalma
});
// A jQuery ajax metódusa egy aszinkron kérést indít,
// amely sikeres válasz esetén meghívja a success-ben
// beállított callback metódust:
$.ajax({
    url: 'login.php',
    data: { username: 'user', password: 'pwd' },
    success: function() {
        console.log('Login successfully!');
    }
});
// Az addEventListener segítségével egy esemény-
// kezelőt köthetünk egy HTML elemre, vagyis egy
// olyan callback függvényt, amely akkor fut le,
// ha az esemény kiváltódik:
document.getElementById('okButton')
    .addEventListener("click", function() {
        console.log('Clicked on OK button!');
    }, false);

Closure

Az a tulajdonság, hogy egy változó a definiálás helyének függvény scope-jában érhető el, egy meglepő, mindazonáltal hasznos mellékhatással is jár. Képzeljük el azt az esetet, hogy deklarálunk egy változót, azonban ugyanezen a függvényen belül egy callback függvényt is kötünk valamilyen eseményhez. Egészen addig nem érdekes a történet, amíg a változót nem használjuk fel a callback-ben, azonban mi történik ha mégis?

Forráskód
var setHeroGreeting = function() {
    var hero = "Superman";
    $('#greet').bind('click', function() {
        alert(hero);
    });
};
setHeroGreeting();

A fentiekben létrehoztunk egy inicializáló-függvényt, ami a greet id-jű html elem klikkelés eseményére köt egy eseménykezelőt — ha valaki rákattint, akkor egy figyelmeztető ablak jelenik meg azzal a szöveggel, hogy „Superman”. Az igazán érdekes mozzanat a kódban az, hogy a hero változó a setHeroGreeting függvényen belül létezik, melynek a futása az eseménykezelő beállítása után nagyon gyorsan véget is ér — azonban a változónak még léteznie kell, hiszen az eseménykezelő akár percekkel később is felhasználhatja azt.

A fenti viselkedés úgy valósul meg, hogy a JavaScript bármely változója elérhető addig, amíg egy referencia mutat rá. Amint megszűnik ez a kötés, úgy a futtatókörnyezet memóriafelszabadításért felelős egysége, a Garbage collector véglegesen törölheti a memóriából. Ezt a jelenséget closure-nek nevezzük és gyakran találkozhatunk vele, hiszen az alkalmazás szintű privát változóinkat és metódusainkat mindig érinti.

Ha a fenti példa után valamikor egy $('#greet').unbind('click'); paranccsal eltöröljük az eseménykezelőnket, akkor abban a pillanatban nem mutat több referencia a hero változóra, és a Garbage collector nyugodt szívvel törölheti azt.

A closure jelenségét érdemes mindig szem előtt tartani, hiszen a feleslegesen megtartott referenciák jelentős mértékben pazarolhatják a memóriát!

Vissza a tartalomjegyzékhez

Függvény, mint objektum

A függvény, mint nyelvi elem nem sokban tér el a hagyományos JavaScript objektumoktól, két markáns különbség azonban mégis akad.

Az első természetesen az, hogy meghívható. Ez egészen egyértelmű és sok példát láttunk rá korábban. A második azonban sokkal izgalmasabb: a new kulcsszóval egy új példány hozható létre a függvényből, egész pontosan klónozható. Utóbbi esetben a függvényt úgynevezett konstruktorfüggvénynek hívjuk és konvenció szerint nagy kezdőbetűvel nevezzük el. A konstruktorfüggvény magjában használt this arra az objektumpéldányra mutat, melyet a new kulcsszó után létre fogunk hozni.

Forráskód
var Hero = function(name) {
    this.name = name;
};
var superman = new Hero('Superman'),
    spiderman = new Hero('Spiderman');
assertEquals('Superman', superman.name);
assertEquals('Spiderman', spiderman.name);

Nézzük, hogy pontosan mi is játszódik le a new Hero(name) használatakor:

Bár a JavaScriptben nincsenek osztályok, a konstruktorfüggvényeket mégis sokszor így emlegetjük, hiszen viselkedésükben nagyon hasonlóak a más nyelvekben megismert osztályokhoz. Természetesen soha ne feledjük, hogy ettől még ezek az „osztályok” egyszerű objektumok, csupán konvenció szerint nem így kezeljük őket. Ha valamilyen kód-validálási eszközt használunk, akkor a nagy kezdőbetűvel elnevezett függvények nem konstruktorként való alkalmazása esetén azonnal hibát kapunk, ugyanígy ha egy kisbetűs függvényt akarunk példányosítani!

A függvény mint objektum és függvényhívási minták

Flash lejátszó letöltése

Függvény mint objektum

Prototípus alapú öröklődés

A hagyományos objektum orientált nyelvekhez hasonló öröklődés, úgynevezett pszeudo-klasszikus objektum öröklődés valósítható meg a függvények segítségével. A függvények prototípus alapú öröklődése az objektumoknál már megismert módon működik — a különbség annyi, hogy az Object.create helyett a new kulcsszóval hozunk létre új objektumpéldányt, a konstruktorfüggvény az új objektum valódi inicializáló metódusa lesz, illetve a prototípus beállítását manuálisan kell elintéznünk:

Forráskód
var Hero = function(name) {
    this.name = name;
};
Hero.prototype.getName = function() {
    return this.name;
};
var Superman = function(name) {
    this.name = name;
};
Superman.prototype = new Hero;
Superman.prototype.fly = function() {
    return "Woohooo I'm flying!";
};
var clark = new Superman('Clark Kent');
assertEquals('Clark Kent', clark.name);
assertEquals('Clark Kent', clark.getName());
assertEquals("Woohooo I'm flying!", clark.fly());

Első lépésben létrehozunk egy „ősosztályt”, a Hero lesz a későbbi objektumunk prototípusa. Azokat a metódusokat, melyeket a származtatott „osztályokban” is szeretnénk használni, az ős-objektum prototípusán definiáljuk, így a getName elérhető lesz később a Superman objektumpéldányain is. Utóbbit is létrehozzuk és egy saját metódussal bővítjük: a fly csak a Superman példányokon fog létezni. A this kulcsszóval hozzáadott vagy lekérdezett érték mindig az objektumpéldány sajátja lesz, nem osztozik rajta a többi példánnyal, így lehet példányváltozó a name.

Talán a fentiekből és az objektumpéldány konstruálási szabályaiból már egyértelművé vált, azonban nem árt ha külön is kitérünk rá: egy metódust csak a konstruktorfüggvényben is elérhető this-el vagy az ős-objektum prototípusán tudunk létrehozni. A magyarázat világos, ha újra megnézzük a new működési elvét: egyszerűen létrehoz egy új objektumot, beállítja a prototípusát és lefuttatja rajta a konstruktorfüggvényt — vagyis a konstruktor-objektum (példánkban a Hero) saját példánymetódusaival nem foglalkozik. Az objektumon közvetlenül definiált metódusoknak mégis van haszna, ezek a klasszikus objektum-orientált struktúrából ismert statikus metódusoknak felelnek meg:

Forráskód
var Hero = function(name) {
    // A heroCount és a getHeroCount statikus tagok,
    // csak a Hero explicit megadásával lehet
    // hivatkozni rájuk!
    Hero.heroCount++;
    this.name = Hero.getHeroCount() + '. ' + name;
};
// Statikus változó deklarálása
Hero.heroCount = 0;
// Statikus metódus deklarálása
Hero.getHeroCount = function() {
    return Hero.heroCount;
};
Hero.prototype.getName = function() { return this.name; };
var superman1 = new Hero('Superman'),
    superman2 = new Hero('Superman');
assertEquals('1. Superman', superman1.getName());
assertEquals('2. Superman', superman2.getName());
// A statikus változók és metódusok nem érhetőek el
// az objektumpéldányokon, csak konkrétan a Hero-ra
// hivatkozva használhatjuk őket:
assertEquals(undefined, superman1.heroCount);
assertEquals(undefined, superman1.getHeroCount);
assertEquals(2, Hero.getHeroCount());

A metódusokat mindig egy adott objektum prototípusán érdemes definiálni, hiszen ilyenkor az egyes objektumpéldányok ugyanazt a metódust fogják használni, nem kell memóriapazarló módon lemásolni minden egyes példányba ugyanazt:

Forráskód
var Hero = function(name) {
    this.name = name;
    // Ez a metódus minden példány számára létre lesz hozva, ami
    // felesleges memóriafoglalással jár, holott az egyes példányok
    // osztozhatnának rajta.
    this.greet = function() { return 'Hi!'; }
}
// Ez a metódus minden objektum-példányban elérhető, azonban csak
// egyszer jön létre, a példányokban csupán referencia mentődik el
// rá. Ez a megoldás jóval memóriakímélőbb.
Hero.prototype.getName = function() {
    return this.name;
};
var hero = new Hero('Superman');
assertEquals('Superman', hero.getName());
assertEquals('Hi!', hero.greet());

Térjünk vissza kicsit az első példánkra, egész pontosan a prototípus definiálására: Superman.prototype = new Hero;.

Első látásra talán egy kicsit furcsa lehet, hogy a Superman prototípusának nem szimplán átadjuk a Hero-t, hanem egy újat példányosítunk belőle. Könnyen belátható azonban, hogy ez miért is történik: ha csak a Hero-t adnánk át, akkor az ő prototípusában található függvények nem lennének elérhetőek a Superman számára, mindenképp egy objektumpéldányra van szükségünk. Egy másik fontos oka is van, hogy a prototípust egy külön objektumpéldányra állítjuk: a Superman prototípust ezután szabadon bővíthetjük, nem fogja módosítani az eredeti Hero objektumot. A JavaScript objektumai futási időben, dinamikusan bővíthetőek, így ha egy objektum prototípusát bővítjük valamivel, akkor ez a módosítás az összes olyan objektumra hatással lesz, amelynek ő a prototípusa!

Forráskód
var Hero = function(name) {
    this.name = name;
};
Hero.prototype.getName = function() { return this.name; };
var Superman = function(name) {
    this.name = name;
};
Superman.prototype = new Hero;
// Felülírjuk a Hero-tól kapott getName metódust,
// azonban ez csak a Superman prototípusában módosul,
// a Hero-hoz nem nyúl!
Superman.prototype.getName = function() {
    return 'Superman: ' + this.name;
};
var sugar = new Hero('Sugarman'),
    clark = new Superman('Clark Kent');
assertEquals('Superman: Clark Kent', clark.getName());
assertEquals('Sugarman', sugar.getName());
// Az ős-objektum prototípusát futási időben, dinamikusan
// bővítve azok az objektumok is módosulnak, amelyeknek ő
// a prototípusa:
Hero.prototype.greet = function() {
    return "Hi I'm " + this.name;
};
assertEquals("Hi I'm Clark Kent", clark.greet());

Bizonyára sokak számára feltűnik, hogy a fenti példákban csak publikus adattagokat láthattunk — vajon mi a helyzet a láthatósággal, hogyan tudunk privát változókat és metódusokat definiálni. Rengeteg technika létezik JavaScriptben arra, hogy elrejtsünk bizonyos értékeket, azonban egyik sem olyan magától értetődő vagy teljes körű, mint egy klasszikus objektum-orientált nyelv esetén. Felvehetjük a változót egy saját névtérbe, vagy elfedhetjük egy trükkös implementációval, azonban a kérdés adott marad: szükségünk van-e egyáltalán arra, hogy adatokat vagy metódusokat rejtsünk el a külvilág elől. Ha a tiszta kód elveit követjük, akkor a privát függvények tobzódása általában azt jelenti, hogy az „osztályunkban” egy másik bújik meg, amelyet érdemes lenne kiemelni egy másik objektumba. Ha mégis szeretnénk egy saját objektumunk valamely elemén privát tulajdonságot használni, akkor konvenció szerint a név elé tett _ (alulvonás) karakterrel jelezzük, hogy azt ne próbálja senki kívülről használni, illetve, hogy feltehetően nincs rá külön egységteszt.

A példákban szereplő Hero és Superman konstruktorfüggvények törzse megegyezik, ami egy elég veszélyes kódduplikáció, hiszen bármilyen módosítás esetén valószínű az összes előfordulásnál módosítani kellene a kódot. Bár natív megoldást, Super metódust nem kapunk arra, hogy elérjük az ős-objektum konstruktorát, azonban egy kis trükkel mégis futtathatjuk azt a „gyerek osztály” konstruktorában is:

Forráskód
var Hero = function(name) {
    this.name = name;
};
var Superman = function(name) {
    Hero.call(this, name);
};
var clark = new Superman('Clark Kent');
assertEquals('Clark Kent', clark.name);

A függvényeken nem csak mi deklarálhatunk metódusokat, néhány előre-definiáltat is kapunk, ilyen pl. a call is, ami egy kis csavarral hívja meg a függvényt. Első paramétereként megadhatjuk, hogy a függvényen belül mire is mutasson a this, majd ezt követően felsorolhatjuk az argumentumokat, amiket egyébként is átadnánk. Ennek köszönhetően a Hero konstruktorfüggvény törzse úgy fut le, mintha a Superman függvénybe lenne „másolva”, így a this.name a Superman példányára fog mutatni. Az ős-objektum konstruktorát ezután szabadon módosíthatjuk, minden tőle származó objektum követi a változást.

Vissza a tartalomjegyzékhez

A this jelentése

A JavaScriptben — sok más nyelvvel ellentétben — a this több különböző jelentést is felvehet, attól függően, hogy hol hívjuk meg, illetve, hogy strict módban vagyunk-e. Alapvetően a this arra az objektumra utal, aminek a kontextusában éppen meghívjuk, azonban lehetőségünk van ezt a viselkedést befolyásolni.

Globális kontextus

A globális kontextusban, vagyis minden függvényen kívül használva, a this a globális objektumra mutat.

Forráskód
// A böngészőkben a globális objektum a window
assertEquals(this, window);
this.hero = 'Superman';
assertEquals('Superman', window.hero);

Függvény kontextus

Egy függvényen belül mindig a hívás helyétől és módjától függ a this értéke.

Egyszerű függvényhívás törzsében

Egyszerű függvényhívás esetén strict módban undefined lesz a this értéke, egyébként pedig referencia a globális objektumra.

Forráskód
var sillyHero = function() {
        return this;
    },
    betterHero = function() {
        "use strict";
        return this;
    };
assertEquals(window, sillyHero());
assertEquals(undefined, betterHero());

Strict módban véletlenül sem tudjuk módosítani a globális scope-ot, használata ezért is erősen ajánlott.

Objektum metódusában

Ha egy függvényt egy objektum metódusaként hívunk meg, akkor a this értéke az az objektum lesz, amin a függvényt meghívtuk.

Forráskód
var hero = {
    name: 'Superman',
    getName: function() {
        return this.name;
    }
};
assertEquals('Superman', hero.getName());

Érdemes megjegyezni, hogy ezt a viselkedést nem befolyásolja az, hogy a függvényt hol és hogyan deklaráltuk, csak az számít, hogy hogyan hívjuk meg:

Forráskód
var hero = {
    name: 'Superman'
};
function getName() {
    return this.name;
};
// A függvényre mutató referenciát később adjuk hozzá a
// hero objektumhoz, ha így, metódusként hívjuk meg
// akkor a this az objektumra fog mutatni:
hero.getName = getName;
assertEquals('Superman', hero.getName());
assertEquals(undefined, getName());

Ha létrehozunk egy függvényt és azt egy változóhoz vagy egy objektum adattagjához rendeljük, akkor — a többi objektumhoz hasonlóan — csupán a rámutató referenciát mentjük el. Ez utóbbi azért is hasznos tulajdonság, mert egy metódust minden további nélkül átadhatunk egy másik objektumnak, a this már az új objektumra fog mutatni.

Forráskód
var strongHero = {
        name: 'Superman',
        getName: function() { return this.name; }
    },
    fatHero = {
        name: 'Sugarman'
    };
fatHero.saySomething = strongHero.getName;
assertEquals('Sugarman', fatHero.saySomething);

Ha egy prototípus alapú öröklődést valósítunk meg — függetlenül attól, hogy az Object.create vagy konstruktorfüggvény alapú — a this mindig a prototípusláncban a híváshoz legközelebbi objektumra fog mutatni! Ez a tulajdonság nagyon jól kihasználható, hiszen egy nagy prototípuslánc segítségével felépített objektumon bármilyen függvényt is hívunk meg, nyilván azt szeretnénk, hogy a példányváltozókat ne a függvény definiálás helyén keresse (ami akár a prototípuslánc mélyén is lehet), hanem a keresést a lánc tetejétől indítsa.

Forráskód
var hero = {
        getName: function() { return this.name; }
    },
    superman = Object.create(hero);
superman.name = 'Clark Kent';
// Bár a superman objektumban nincs getName függvény, azt
// a prototípuslánc következő elemétől kell elkérnie, azonban
// maga a hívás a superman objektumon történt, így a this
// értéke is a superman-től indul. Ha itt nem találna name
// property-t, akkor tovább keresné a prototípusláncban.
assertEquals('Clark Kent', superman.getName());

A metódusokra vonatkozó szabályok érvényesek az EcmaScript 6-ban bevezetésre kerülő Getterek és Setterek esetén is:

Forráskód
var hero = {
    name: 'Superman',
    get greeting() {
        return "Hi I'm " + this.name;
    }
};
assertEquals("Hi I'm Superman", hero.greeting);

Figyeljünk oda arra, hogy egy metódusbeli belső függvény már egyszerű függvénydefiníciónak minősül, így az ott szereplő this értéke a globális objektum (strict módban pedig undefined) lesz! Könnyű megfeledkezni erről, hiszen az az ember érzése, hogy egy objektumon belül van.

Forráskód
var name = 'Sugarman',
    hero = {
        name: 'Superman',
        getIntroductionHTML: function() {
            var formattedName = function(tag) {
                return '<' + tag + '>' + this.name + '</' + tag + '>';
            };
            return '<div class="name">' +
                      formattedName('span') +
                   '</div>';
        }
    }
// A belső formattedName függvényben hiába használjuk a this.name
// változót, a this a globális objektumra fog mutatni, így hibás
// értéket kapunk vissza.
assertEquals(
    '<div class="name"><span>Sugarman</span></div>',
    hero.getIntroductionHTML());

A példa formattedName belső függvényében hibásan használjuk a this.name értéket, a this a globális objektumra fog mutatni, így rossz nevet kapunk. Ha strict módban dolgozunk, akkor a fenti példa a getIntroductionHTML metódus hívásánál egy TypeError-t dob, hiszen a this értéke undefined lesz. Utóbbi egy újabb példa arra, hogy érdemes mindig strict módot használni, hiszen egy valódi hibát könnyebben találhatunk meg és javíthatunk ki, mint egy hibás vagy következetlen működést!

Konstruktorfüggvény törzsében

Ha egy konstruktorfüggvényt — helyesen — a new kulcsszóval hívunk meg, akkor a this mindig a létrehozandó, új objektumra fog mutatni. Amennyiben a függvénytörzsben nem definiálunk egy konkrét visszatérési értéket, akkor automatikusan a this értékét kapjuk vissza.

Forráskód
var Hero = function(name) {
    this.name = name;
}
var superman = new Hero('Superman');
assertEquals('Superman', superman.name);
var SuperHero = function(name) {
    this.name = name;
    return {
        name: 'SuperHero!!!'
    };
}
var superhero = new SuperHero('Sugarman');
assertEquals('SuperHero!!!', superhero.name);

Az utóbbi esetben, mikor is a konstruktorfüggvény visszatérési értékét felülírjuk, a this-t tartalmazó sorok a függvény lefutása után elvesznek. Mivel a JavaScript ezen tulajdonsága igen ritkán használatos, így sokszor kellemetlen meglepetést tudunk vele okozni a kollégáinknak — többek között ezért is kerüljük a használatát.

Megadott kontextus

A JavaScript — a Function objektum prototípusán — három különböző metódust biztosít arra, hogy saját magunk állíthassuk be a this jelentését — más szóval, mi szabhassuk meg a függvény futási kontextusát.

A call és az apply

Mindkettő metódus egyszerűen meghívja a függvényt — mintha csak mi tennénk a hagyományos módon — azonban az első paraméterükben megadható, hogy mi legyen a futási kontextus, vagyis a this mire is mutasson. A különbség közöttük a meghívandó függvény paramétereinek átadásában van: míg a call paraméterlistája az eredeti függvényére hasonlít, azaz sorban, hagyományos paraméterként kell átadnunk a függvény argumentumait, addig az apply esetén az összes argumentumot egy tömbben adjuk át a második paraméter helyén.

Forráskód
var heroGreeting = function(address, greeting) {
    greeting = greeting || "Hi I'm ";
    return greeting + this.name + ' from ' + address;
};
var superman = {
        name: 'Superman',
        fly: function() {
            return this.name + ' is flying!';
        }
    },
    Sugarman = function() {
        this.name = 'Sugarman';
    },
    sugarman = new Sugarman();
// Az első paraméterben definiáljuk, hogy mire mutasson a
// függvényen belüli this:
assertEquals("Hi there, I'm Superman from Kripton",
       heroGreeting.call(superman, 'Kripton', "Hi there, I'm "));
// Az apply teljes mértékben megfelel a call-nak, de a heroGreeting
// argumentumait itt egy tömbben kell átadni:
assertEquals("Hi there, I'm Superman from Kripton",
       heroGreeting.apply(superman, ['Kripton', "Hi there, I'm "]));
// Bármilyen objektumon meghívható, amely rendelkezik
// name nevű példányváltozóval:
assertEquals("Hi I'm Sugarman from Earth",
       heroGreeting.call(sugarman, 'Earth'));
// Kölcsönözhetünk metódust másik objektumtól is:
assertEquals('Sugarman is flying!', superman.fly.call(sugarman));

A két metódus közötti választás általában egyszerű, a kódolási helyzet miatt sokszor adja is magát. A call metódus egy, maximum két paraméter esetén eredményez olvashatóbb kódot, az apply pedig nagyon egyszerűvé teszi a dinamikus paraméterlisták felépítését és átadását a függvénynek.

Bár sokszor jól jön, hogy egy metódust egy objektumtól kölcsönzünk és a call vagy az apply segítségével egy másik objektumon használjuk fel — legtöbbször mégis a beépített metódusokkal való ügyes kombinációjuk bizonyul hasznosnak. Sok esetben nagyobb ciklusok, függvények kerülhetőek el a segítségükkel:

Forráskód
var numbers = [5, 6, 2, 3, 7];
// Keressük meg a legnagyobb értéket a tömbben. A
// Math.max paramétereiben számokat vár, és azok
// közül választja ki a legnagyobbat. Az apply tökéletes
// választás, hiszen a tömböt argumentumokra bontja
// és így hívja meg a Math.max-ot:
var max = Math.max.apply(null, numbers);
assertEquals(7, max);

A fenti példában láthatjuk, hogyha a függvény futási kontextusa nem számít (pl. csak az argumentumaival foglalkozik vagy nem található benne hivatkozás a this-re), akkor egyszerűen null-t (vagy undefined-ot) is átadhatunk neki, ekkor a függvény a globális névtérben fog futni.

A bind metódus

Sok esetben szükségünk van arra, hogy egy függvény (tipikusan callback) egy másik kontextusban fusson, de még ne hívódjon meg. Erre a problémára kínál elegáns megoldást a bind metódus, amely a paraméterül kapott objektumot a függvény kontextusaként fogja meghatározni úgy, hogy bármikor is hívjuk meg azt a megszokott módon, a kontextus mindig a beállított legyen.

Az ES5 előtti időkben sokszor a this értékét egy _this, that vagy self nevű változóba mentettünk el, hogy a callback metódusokban is elérhető legyen az előző scope:

Forráskód
var name = 'Sugarman',
    hero = {
        name: 'Superman',
        getIntroductionHTML: function() {
            var _this = this,
                formattedName = function(tag) {
                    return '<' + tag + '>' + _this.name + '</' + tag + '>';
                };
            return '<div class="name">' +
                      formattedName('span') +
                   '</div>';
        }
    }
assertEquals(
    '<div class="name"><span>Superman</span></div>',
    hero.getIntroductionHTML());

A bind segítségével nincs szükség a this külön elmentésére, hiszen mi határozhatjuk meg a függvénybeli kontextust:

Forráskód
getIntroductionHTML: function() {
    var formattedName = (function(tag) {
            return '<' + tag + '>' + this.name + '</' + tag + '>';
        }).bind(this);
    return '<div class="name">' +
              formattedName('span') +
           '</div>';
}

A fat arrow kontextusa

Az ES6-ban bemutatkozó fat arrow operátor olyan függvényt hoz létre, melynek kontextusa mindig az a scope, amiben őt definiáltuk. E tulajdonság miatt sok esetben nagyon expresszív kódot kaphatunk:

Forráskód
var hero = {
        name: 'Superman',
        getIntroductionHTML: function() {
            var name = (tag) => '<' + tag + '>' + this.name + '</' + tag + '>';
            return '<div class="name">' +
                      name('span') +
                   '</div>';
        }
    }
assertEquals(
    '<div class="name"><span>Superman</span></div>',
    hero.getIntroductionHTML());

Vissza a tartalomjegyzékhez

Generátor-függvények

Az EcmaScript 6-ban megjelenő generátorok hatékony eszközök olyan metódusok írására, melyek valamilyen állapotot őriznek és minden hívásra továbblépnek egy következő állapotba. Mielőtt azonban részletesebben megnéznénk mi az és mire is szolgál egy generátorfüggvény, helyezzük kontextusba az iterátorok megismerésével.

A collection típusokban való iteráció egy nagyon gyakori művelet, nem is csoda, ha egyes programozási nyelvek külön nyelvi elemeket biztosítanak arra, hogy megkönnyítsék. A JavaScriptbe az ES5-el több olyan metódus is bekerült, ami ezt a célt szolgálja, elég ha csak a map, a filter vagy a reduce függvényekre gondolunk. Néha azonban ez sem elég, szeretnénk olyan saját típusokat, objektumokat létrehozni, ahol mi mondhatjuk meg, hogy hogyan is iteráljon végig rajta a futtatókörnyezet, ahol mi határozzuk meg, hogy hogyan is viselkedjen például egy ciklusban.

Szerencsére az EcmaScript 6-ban több lehetőségünk is van olyan adatszerkezet létrehozására, amely natívan kihasználja a beépített iterációs elemeket, pl. szerepelhet a for..in vagy a for..of ciklusokban, mint iterátor.

Egy iterátor egy olyan adattípus, ami valamilyen collection-höz kapcsolódva képes annak adattagjain egyesével végiglépni, azaz végig-iterálni — nyilvántartja, hogy épp hol tart az objektumban tárolt adatok között, illetve, hogy hogyan lehet a következő adatot lekérni.

A JavaScriptben minden iterátor rendelkezik egy next metódussal, amelyet meghívva a hozzá kapcsolódó collection aktuális elemét kapjuk vissza, majd eggyel előrébb lépteti az adattagokra mutató referenciát. A visszaadott érték { value: 0, done: false } formátumú, ahol a value a valós visszatérési értéket fogja tartalmazni, míg a done azt, hogy befejeződött-e az iterálás, végig értünk-e az adatokon.

Az iterátorokkal expresszív függvények készíthetőek olyan feladatokra, ahol egy adatszerkezet elemein kell végighaladni valamilyen speciális módszer segítségével, vagy ahol iteratívan generálhatunk értékeket. Előbbire példa egy fa-struktúra, melynek elemein egy iterátor mélységi, míg egy másik iterátor szélességi bejárással fut végig.

A generálásra egyszerű példa a Fibonacci számok esete, amelyeket elvileg a végtelenségig generálhatnánk, azonban ez viszonylag ritka eset. Egy iterátorral olyan metódust készíthetünk, ami minden next hívásra a következő Fibonacci számot adja vissza, így öt hívásra a sorozat első öt elemét kapjuk meg, a hatodik már nem lesz kiszámolva. Az iterátor-metódusunk őrzi az állapotát, bármikor újabb számot kérhetünk tőle és onnan folytatja, ahol abbahagyta!

Az EcmaScript 6 bevezeti a generátorfüggvények fogalmát, amely egy egyszerűsített szintaxist takar az iterátorok létrehozására. Egy generátor bármilyen hagyományos JavaScript kódot tartalmazhat, csupán néhány ponton különbözik a többi függvénytől:

Forráskód
var superHeroGenerator = function* () {
    yield 'Batman';
    yield 'Superman';
};
var getSuperHero = superHeroGenerator();
assertEquals({ value: "Batman",   done: false }, getSuperHero.next());
assertEquals({ value: "Superman", done: false }, getSuperHero.next());
assertEquals({ value: undefined,  done: true  }, getSuperHero.next());

A visszatérési érték done adattagjára oda kell figyelni, ugyanis a true érték után befejeződik a generátor, minden további next hívás hibát dob.

Általában nem a next kézzel való hívogatása a generátorok preferált felhasználási módja, hiszen a for..of ciklussal könnyedén végig-iterálhatunk az értékeiken. Ekkor az iterátor végállapotára sem kell figyelni, a futtatókörnyezet megteszi azt helyettünk:

Forráskód
for (var superhero of superHeroGenerator()) {
    console.log(superhero);
}
// => Batman
// => Superman

Egy generátorfüggvény által létrehozott iterátor a „megállíthatóságán” túl nem különbözik a többi JavaScript metódustól, vagyis egyirányú — nincs natív lehetőség a visszalépére vagy a metódus elejére ugrásra.

Ha szükség van rá, akkor a next metódusnak egy paramétert is átadhatunk, melyet az iterátor az előző yield utasítás visszatérési értékeként kapja meg.

Forráskód
var superHeroGenerator = function* () {
    var postfix = yield 'Batman';
    yield 'Super' + postfix;
};
var getSuperHero = superHeroGenerator();
assertEquals({ value: "Batman",   done: false }, getSuperHero.next());
assertEquals({ value: "Superwoman", done: false }, getSuperHero.next('woman'));
assertEquals({ value: undefined,  done: true  }, getSuperHero.next());

A generátorfüggvények izgalmas tulajdonsága, hogy végtelen futású metódusok alkothatóak velük. Nézzünk egy egyszerű példát erre:

Forráskód
var naturalNumberGenerator = function* () {
    var n = 1;
    while (true) {
        yield n++;
    }
}
for (var number of naturalNumberGenerator()) {
    console.log(number);
    if (number == 3) break;
}
// => 1
// => 2
// => 3

Természetesen figyeljünk oda, hogy a fentihez hasonló iterátorokat ciklusban használva kívülről gondoskodjunk a megszakításukról, így elkerülhetjük a végtelen ciklust.

Bár kis kreativitással rengeteg hasznos területet el lehet képzelni a generátorok számára, a legnépszerűbb felhasználási módjuk azonban minden bizonnyal az aszinkron műveletek lineárissá tételében van. A későbbiekben meglátjuk, hogy hogyan is segíthetnek nekünk a generátorfüggvények az Ajax kérések és eseménykezelők okozta kuszaság kibogozásában.

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.