[JS] scope

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • orf
  • Registratie: Augustus 2005
  • Laatst online: 23:27
Om dynamisch events toe te voegen gebruik ik addEvent() van Dean Edwards / Tino Zijdel.
Dat werkt prima, maar ik heb een probleempje met de scope waar ik niet helemaal uit kom.

Kleine testcase:

HTML:
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
38
39
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">    
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
        <title></title>
        <script type="text/javascript" src="js/addevent.js"></script>
        <script type="text/javascript">
        function cTest(){
        
            this.sTestVar = '';
            
            this.init = function(){
                addEvent(document.getElementById('testselect'), 'change', this.alert);   
                // alert onchanged niet de value die ik wil.
            }
            this.alert = function(){
                alert('this.sTestVar = ' + this.sTestVar);
            }
        }

        
        function load(){
            var oTest = new cTest();
            oTest.sTestVar = 'Test!';
            oTest.init();
            oTest.alert();
            // geeft de juiste alert
        }
        addEvent(window, 'load', load);
        </script>
        
    </head>
    <body>
    <select id="testselect">
        <option>1</option>
        <option>2</option>
    </select>
    </body>
</html>


Bij deze testcase krijg je onload een alert met de juiste tekst: Test!
Wanneer het selectfield changed is, krijg je wel een alert, maar zonder tekst (undefined). Hoe kan ik ervoor zorgen dat dit wel in de juiste scope uitgevoerd wordt?

Dank! :)

[ Voor 3% gewijzigd door orf op 05-11-2008 22:42 ]


Acties:
  • 0 Henk 'm!

  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Je moet een vorm van closure gebruiken om je alert-method in de juiste scope uit te voeren; bijvoorbeeld:
JavaScript:
1
2
3
4
this.init = function(){
    var self = this;
    addEvent(document.getElementById('testselect'), 'change', function() { self.alert(); });   
}


zelf gebruik ik een binder-helper:
JavaScript:
1
2
3
4
5
6
7
8
9
Function.prototype.bind = function()
{
    var handler = this, args = [].slice.call(arguments, 0), obj = args.shift();

    return function()
    {
        return handler.apply(obj, args.concat([].slice.call(arguments, 0)));
    }
}

en dan kan je dit doen:
JavaScript:
1
2
3
this.init = function(){
    addEvent(document.getElementById('testselect'), 'change', this.init.bind(this));   
}

Intentionally left blank


Acties:
  • 0 Henk 'm!

  • orf
  • Registratie: Augustus 2005
  • Laatst online: 23:27
Oef, dat is mooi spul. Ik had al wat gerommeld met self = this, maar bind is wel heel chique!
Dat maakt een hoop code een stuk leesbaarder. Dank Tino!

Acties:
  • 0 Henk 'm!

  • Rekcor
  • Registratie: Februari 2005
  • Laatst online: 05-09 21:08
crisp schreef op woensdag 05 november 2008 @ 22:53:
Je moet een vorm van closure gebruiken om je alert-method in de juiste scope uit te voeren; bijvoorbeeld:
JavaScript:
1
2
3
4
this.init = function(){
    var self = this;
    addEvent(document.getElementById('testselect'), 'change', function() { self.alert(); });   
}
Inderdaaad _/-\o_.

Toch begrijp ik niet waarom

JavaScript:
1
2
3
4
this.init = function(){
    var self = this;
    addEvent(document.getElementById('testselect'), 'change', function() { this.alert(); });   
}


niet werkt. Waarom verwijst this in

JavaScript:
1
var self = this; 


wel naar het object, en niet alleen naar de functie this.init?

[ Voor 8% gewijzigd door Rekcor op 06-11-2008 08:22 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Rekcor schreef op donderdag 06 november 2008 @ 08:21:
Waarom verwijst this in

JavaScript:
1
var self = this; 


wel naar het object, en niet alleen naar de functie this.init?
this is geen variabele. self heeft binnen de inner function de waarde die is toegekend in de outer function (zo werken closures). this echter wordt binnen elke functie opnieuw geevalueerd en krijgt een specifiek object toegekend als waarde. Grofweg:

• een DOM element als het een event handler is
• een JS object instance als het een method is
window (beter gezegd de top global) in andere gevallen.

Acties:
  • 0 Henk 'm!

  • UltimateB
  • Registratie: April 2003
  • Niet online

UltimateB

Pomdiedom

Die bind heeft wel zijn voordelen... Zou me niet verbazen als de callback functie van google maps stiekem het zeflde gebruikt.

"True skill is when luck becomes a habit"
SWIS


Acties:
  • 0 Henk 'm!

Verwijderd

UltimateB schreef op donderdag 06 november 2008 @ 09:58:
Die bind heeft wel zijn voordelen...
Ik ben er persoonlijk niet zo'n fan van. De code wordt compacter maar het verbergt iets vrij simpels achter een enorme berg acabadabra (imho).

Acties:
  • 0 Henk 'm!

  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Verwijderd schreef op donderdag 06 november 2008 @ 12:35:
[...]

Ik ben er persoonlijk niet zo'n fan van. De code wordt compacter maar het verbergt iets vrij simpels achter een enorme berg acabadabra (imho).
Een simpel truukje is nog geen 'hele berg' abracadabra, complete frameworks zoals prototype en jQuery die door legio mensen gebruikt worden zijn pas een hele berg abracadabra :P

Intentionally left blank


Acties:
  • 0 Henk 'm!

Verwijderd

crisp schreef op donderdag 06 november 2008 @ 12:46:
Een simpel truukje is nog geen 'hele berg' abracadabra, complete frameworks zoals prototype en jQuery die door legio mensen gebruikt worden zijn pas een hele berg abracadabra :P
Het komt een beetje omdat het een soort "trust me, it works"-code is. Ik heb met heel veel JS-programmeurs samengewerkt maar ik denk dat nog geen 5% het volgende stuk code kan doorgronden:
JavaScript:
1
2
3
4
5
6
7
8
9
Function.prototype.bind = function()
{
    var handler = this, args = [].slice.call(arguments, 0), obj = args.shift();

    return function()
    {
        return handler.apply(obj, args.concat([].slice.call(arguments, 0)));
    }
}

Prototype en jQuery zijn zeker bergen acabadabra, maar de acabadabra-dichtheid van deze functie is wel heel erg groot ;) Ik snap wat er gebeurt maar pas na een paar keer lezen.

Mijn mening hierover komt heel erg door mijn persoonlijke smaak: ik zie liever wat langere stukken code die iedereen in mijn team kan snappen dan compacte juweeltjes (hoe mooi ook).

Overigens betekent het niet dat ik bind() niet handig vind. Het heeft z'n plek verdient maar ik ben er geen fan van.

Acties:
  • 0 Henk 'm!

  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Nou ja, de abracadabra zit 'm voornamelijk in het stukje:
JavaScript:
1
[].slice.call(arguments, 0)

Dat is een noodzakelijk kwaad aangezien arguments standaard geen Array instance is... Verder is het redelijk straight-forward volgens mij.

Intentionally left blank


Acties:
  • 0 Henk 'm!

Verwijderd

crisp schreef op donderdag 06 november 2008 @ 15:08:
Verder is het redelijk straight-forward volgens mij.
Ja maar jij bouwt DHTML Lemmings-klonen. Not my typical team member :+

Acties:
  • 0 Henk 'm!

  • Xirt
  • Registratie: December 2003
  • Laatst online: 21-09 09:14
Verwijderd schreef op donderdag 06 november 2008 @ 14:36:
[...]
Mijn mening hierover komt heel erg door mijn persoonlijke smaak: ik zie liever wat langere stukken code die iedereen in mijn team kan snappen dan compacte juweeltjes (hoe mooi ook).

Overigens betekent het niet dat ik bind() niet handig vind. Het heeft z'n plek verdient maar ik ben er geen fan van.
Ondanks dat ik de meeste scripts die ik tegenkom (soms na wat puzzelen) wel kan doorgronden, moest ik ook even goed kijken wat er gebeurd in bovenstaand scriptje. Ik ben het dan ook met Blues eens dat je beter eens script kan schrijven dat iets langer (en misschien net iets langzamer is), maar wel leesbaar. Zo kunnen andere mensen het script ook inzien / veranderen als dat nodig is (in de toekomst?). Bovendien is het internet snel genoeg om een paar extra bytes te vervoeren en zijn de computer tegenwoordig ook snel genoeg om een net iets minder efficient script te runnen.

Acties:
  • 0 Henk 'm!

  • DiSiLLUSiON
  • Registratie: September 2000
  • Laatst online: 12-09 18:17
Zelf ben ik het daar niet helemaal mee eens, hoe compacter & eleganter een functie, hoe beter.

Wel kijk ik altijd de kat uit de boom wat het adden aan / veranderen van prototypes betreft. Iedereen heeft dat vast wel eens meegemaakt: zelf een degelijk stuk code geschreven, site gaat online, klant wil een half jaar later iets nieuws, iemand met minder javascript ervaring moet het er in gaan stoppen en gebruikt daarvoor een library dat aan de prototypes rommelt...

Probeer dan maar eens uit te vinden waarom standaard methods opeens anders reageren dan gebruikelijk -- met bugs tot gevolg.

Op het moment dat je redelijk zeker weet dat je als enige (of met collega's die verstand van zaken hebben) bezig zal zijn met de code in question, is het natuurlijk een andere zaak.

[ Voor 13% gewijzigd door DiSiLLUSiON op 08-11-2008 08:57 ]


  • graceful
  • Registratie: Maart 2008
  • Niet online
Een late reply, hoop dat dit geen probleem is.

Ik zat even je code te bekijken, maar het script van crisp was helemaal niet nodig geweest. Je kon gewoon oTest.alert() oproepen. Zie:

code:
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
38
39
40
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
        <title></title>
        <script type="text/javascript">
        var oTest;
        function cTest(){

            this.sTestVar = '';

            this.init = function(){
                document.getElementById('testselect').onchange = function(){
                        oTest.alert();
                }
                // alert onchanged niet de value die ik wil.
            }
            this.alert = function(){
                alert('this.sTestVar = ' + this.sTestVar);
            }
        }


        window.onload = function(){
            oTest = new cTest();
            oTest.sTestVar = 'Test!';
            oTest.init();
            oTest.alert();
            // geeft de juiste alert
        }
        </script>

    </head>
    <body>
    <select id="testselect">
        <option>1</option>
        <option>2</option>
    </select>
    </body>
</html>


Of zie ik iets over het hoofd? :+
Zo behoud je ook de event scope en kun je daar verder mee aan de slag..

  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Ja, maar je hebt nu een global nodig om een referentie naar je object instance in op te slaan...

Intentionally left blank

Pagina: 1