Végrehajtási környezet elvesztése (scope loss)

Egy korábbi fejezetben már volt arról szó, hogy a this kulcsszó egy függvényen belül a hívó által meghatározott, független attól, hogy milyen futási környezetben definiáltuk az adott függvényt.

function F1(){
    this.name = "F1 object";

    this.toString = function(){
        return "I am "+ this.name;
    };
}

function Kutya(name){
    this.name = name;
}

var objF = new F1();
alert(objF); // I am F1 object

var objKutya1  = new Kutya("bundas");
objKutya1.toString = objF.toString;
alert(objKutya1); // I am bundas

A fenti példa azt mutatja be, hogy ugyanaz a függvény különböző futási környezetben is végrehajtható. A toString() függvény első végrehajtásakor a this kulcsszó az objF objektumot testesíti meg, míg második alkalommal már az objKutya1 objektumot.

Végrehajtási kontextus elvesztéséről akkor beszélünk, ha egy függvényünk nem abban a kontextusban fut le, mint amiben azt definiáltuk. Ha a függvényünk egy objektum tulajdonságaira vagy metódusaira hivatkozik a this kulcsszóval, a függvény végrehajtásakor hiba lép fel, mivel a this egy másik objektumra hivatkozik. Fontos kihangsúlyozni, hogy a végrehajtási környezet elvesztéséből csak akkor származhat problémánk, ha a függvényünkben használtuk a this kulcsszót. Lássunk néhány példát.


<script type="text/javascript">
    var App = {
        locales : {
            message : "button clicked"
        },

        init : function(){
            var oBtn = document.getElementById('btnTest');
            oBtn.onclick = function(){
                alert(this.locales.message);
            };
        };
    };

    window.onload = function(){
        App.init();
    };
</script>
</head>
<body>
    <button id="btnTest" accesskey="t">Test</button>
</body>
</html>

Az App objektum init() metódusa egy eseménykezelőt kapcsol a HTML oldalon lévő nyomógombhoz. Az anonim eseménykezelő akkor lesz végrehajtva, amikor a gombot a felhasználó lenyomja. Az eseménykezelő lefutásakor a kívánt üzenet nem íródik ki, helyette egy futási hiba keletkezik ('this.locales.message' is null or not an object). Az eseménykezelő függvény ugyanis más kontextusban hajtódik végre, azaz a this kulcsszó objektum referenciát tartalmaz magára a HTML nyomógombra. Erről könnyen meggyőződhetünk, elég kiíratni a nyomógomb elem azonosítóját (id tulajdonságát).


oBtn.onclick = function(){
    alert(this.id); // btnTest
};

Maradjunk még ennél a példánál, és próbáljuk meghívni az init metódust az oldal betöltése után a következő módon:

window.onload = App.init;

A nyomógombra kattintás után futási hiba keletkezik ugyanazzal a hibaüzenettel (’this.locales.message’ is null or not an object). Sikerült egy újabb példát bemutatni futási környezet elvesztésére. A különbség csak annyi, hogy most az init függvény a globális végrehajtási környezetben van végrehajtva.


var App = {
    locales : {
        message : "button clicked"
    },
   init : function(){
        var oBtn = document.getElementById('btnTest');
        alert(this); // [object Window]
        oBtn.onclick = function(){
            alert(this); // [object HTMLButtonElement]
        };
    }
};
window.onload = App.init;

A window objektum onload tulajdonságához csak egy referenciát rendeltünk az init() függvényre. Így az init() függvény semmit sem fog tudni az App objektumról, a this kulcsszó a globális objektumra (ez általában a window objektum) fog hivatkozni.

A this kulcsszó értéke a kód jellegétől függően állítódik be.
Eval Eval kód esetén a hívó futási környezet this értékét veszi fel.
Függvény Függvény kód esetén a hívó biztosítja a this értékét, viszont ha ez nem egy objektum, akkor a this a globális objektum lesz.
Globális kód Globális kód esetén a this a globális objektum lesz.