Toon posts:

[JS] Scope variable gaat verloren*

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Beste tweakers,

Heb een probleem met de volgende javascript code

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function StateTest(str)
{
    this.str = str;
}

StateTest.prototype =
{
    test: function()
    {
        DoTest(this.callback);
    },

    callback: function()
    {
        alert(this.str);
    }
}

function DoTest(cb)
{
    cb();
}

var t = new StateTest("hello");
t.test();


Als ik dit uitvoer krijg ik een alert met daarin 'undefined' terwijl ik eigenlijk verwacht dat er 'hello' staat. Het lijkt alsof javascript niet de huidige instance van de javascript class meestuurt maar een statische versie van de class.

Acties:
  • 0 Henk 'm!

  • Bosmonster
  • Registratie: Juni 2001
  • Laatst online: 22-09 16:31
Je bent bij de uitvoering van cb() de context van je originele object kwijt. Wat je kunt doen is een ref naar het object meegeven:

JavaScript:
1
DoTest (this, this.callback);


Zodat je deze bij de uitvoering kunt gebruiken middels call/apply:

JavaScript:
1
2
3
4
function DoTest(obj, cb)
{
    cb.call (obj);
}


Call/apply zorgt er in dit geval voor dat je callback weer in de context van het meegegeven object wordt uitgevoerd.

Meer info over call en apply
Meer info over javascript closures (vrij diep)

[ Voor 36% gewijzigd door Bosmonster op 22-09-2008 15:32 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Je verwacht nu dat javascript het zelfde werkt als de meeste andere oo-talen. Javascript is echter een prototype-based taal. Classes bestaan niet, een functie is geen method van een class. De context (de "this") van een functie wordt bepaald door de manier waarop de functie wordt aangeroepen. Het volgende voorbeeld illustreert dit:

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function Cat()
{
    this.sound = "meow";
}

function Dog()
{
    this.sound = "bark";
}

function makeSound()
{
    alert(this.sound);
}

// A global variable
var sound = "moo";

// Create an instance of a Dog and an instance of a Cat
var odie = new Dog();
var garfield = new Cat();

// Make the makeSound function a member of our dog and cat objects
odie.makeSound = makeSound;
garfield.makeSound = makeSound;

// Call the makeSound() function within the context of a Dog object.
odie.makeSound(); // --> "bark"

// Call the makeSound() function within the context of a Cat object.
garfield.makeSound(); // --> "meow"

// Call the makeSound() function within the global context.
makeSound(); // --> "moo"

// Call the makeSound() function within a new context, "sound" is not a defined field in this context.
var x = new makeSound(); // --> "undefined"


In jouw voorbeeld wordt de callback functie binnen de globale context uitgevoerd. Voeg maar eens het volgende toe aan het begin van jouw code:
JavaScript:
1
var str = "hey!";


Overigens heeft javascript een krachtige feature om te doen wat jij wil: closures. Als je closures zou gebruiken, dan zou jouw code er ongeveer als volgt uit zien:

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function StateTest(str)
{
    this.callback = function()
    {
        alert(str);
    }
    
    this.test = function()
    {               
        DoTest(this.callback);
    }
}

function DoTest(cb)
{
    cb();
}

var t = new StateTest("Hello");
t.test();


Je zou eens moeten kijken naar de presentatie "Advanced Javascript" van Douglas Crockford. Daar legt hij heel goed uit hoe je van closures gebruik kunt maken.

[ Voor 0% gewijzigd door Verwijderd op 22-09-2008 15:44 . Reden: typo ]


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Bedankt voor de reacties.

Ik dacht dat mijn manier het zelfde was als closure alleen dan een andere manier van opschrijven.

Volgens mij is dit niet helemaal het geval en na het die presentatie te bekijken is mij duidelijk dat je op de closure manier veel meer kan en dat het veel meer lijkt op een daadwerkelijke class.

Heb het nu zo opgelost, niet helemaal de manier van Douglas Crockford omdat ik wel 'new' wil gebruiken bij aan aanmaken van een nieuwe instance.

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
        // Constructor
        function StateTest(str)
        {
            // Private variables
            var _privateVar = "private";

            // Private functions
            function privateFunction()
            {
                alert(_privateVar);
            }

            return {
                // Public variables
                publicVar: "publicVar",

                // Public functions
                test: function()
                {
                    DoTest(this.callback);
                },

                callback: function()
                {
                    alert(str);
                    privateFunction();
                }
            }
        }

        function DoTest(cb)
        {
            cb();
        }

        var t = new StateTest("StateTest Hello");
        t.test();


Ook kan ik nu private functions en variables gebruiken _/-\o_