Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[JS] scope probleempje

Pagina: 1
Acties:
  • 105 views sinds 30-01-2008
  • Reageer

  • Mei
  • Registratie: Juni 2005
  • Laatst online: 17-10-2024
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
function Request(sMethod, sURL, bAsync, sResponseType, sCallback, aCallbackArguments, sOnError, aOnErrorArguments)
{
    this.sMethod=sMethod;
    this.sURL=sURL;
    this.bAsync=bAsync;
    this.sResponeType=sResponseType;
    this.sCallback=sCallback;
    this.aCallbackArguments=aCallbackArguments;
    this.sOnError=sOnError;
    this.aOnErrorArguments=aOnErrorArguments;
    setStatus('loading', 'progress');
    if (window.XMLHttpRequest)
    {
        this.oHttpRequest=new XMLHttpRequest();
    }
    else if (window.ActiveXObject)
    {
        this.oHttpRequest=new ActiveXObject("Microsoft.XMLHTTP");
    }
    this.oHttpRequest.onreadystatechange=this.checkStatus;
    this.oHttpRequest.open(sMethod, sURL, bAsync);
    this.oHttpRequest.send(null);
    
    if(typeof Request._initialized=='undefined')
    {
        Request.prototype.checkStatus=function()
        {
            alert(this);

Het probleem zit hem bij het aanroepen van de checkStatus() method als eventhandler van het onreadystatechange event. Pas bij de tweede keer instancen van dit object wordt de method ook daadwerkelijk aangeroepen, maar dan heeft deze geen toegang tot de instance met 'this'. Deze verwijst naar de method zelf.

Ik ben al weet niet hoe lang bezig dit werkend te krijgen vanochtend en heb op allerlei manieren geprobeerd de method aan te roepen en te zorgen dat hij over de properties van de instance beschikt, maar het lukt niet. Telkens zit ik weer met een scope probleem waar ik niet achter kom. Ik had gehoopt dat iemand hier me een duwtje in de goede richting kon geven :)

  • marty
  • Registratie: Augustus 2002
  • Laatst online: 27-03-2023
weet niet zeker of het werkt, maar dacht iets van dit:

code:
1
2
3
4
5
6
Request.prototype.checkStatus=checks(this)

function checks(obj)
{
  return function() { alert(obj); }
}

  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

http://therealcrisp.xs4al...-and-the-scope-of-events/

kortom: je zal expliciet moeten scopen, en sowieso als je je Request object instance als scope wilt hebben. Ik gebruik daarvoor de handige bind() method geleend van prototype:

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
Object.extend = function(dest, source, allowOverwrite)
{
    for (var prop in source)
    {
        if (source.hasOwnProperty(prop) && (allowOverwrite || !dest.hasOwnProperty(prop)))
            dest[prop] = source[prop];
    }

    return dest;
}

Object.extend(Function.prototype,
{
    bind: function()
    {
        var handler = this, args = [].slice.call(arguments, 0), object = args.shift();

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

function Request(sMethod, sURL, bAsync, sResponseType, sCallback, aCallbackArguments, sOnError, aOnErrorArguments)
{
  // ...

  this.oHttpRequest.open(sMethod, sURL, bAsync);
  this.oHttpRequest.onreadystatechange = this.checkStatus.bind(this);
  this.oHttpRequest.send(null);

  // ...
}


Let ook op op de plek van het toekennen van de handler, na de open() method. Sommige browsers resetten de handler na het aanroepen van open().

Intentionally left blank


  • JKVA
  • Registratie: Januari 2004
  • Niet online

JKVA

Design-by-buzzword fanatic

crisp schreef op donderdag 09 augustus 2007 @ 12:00:
http://therealcrisp.xs4al...-and-the-scope-of-events/

kortom: je zal expliciet moeten scopen, en sowieso als je je Request object instance als scope wilt hebben. Ik gebruik daarvoor de handige bind() method geleend van prototype:
Hmm, ik hoor je de laatste tijd al een paar keer over prototype. Gebruik je het framework nu zelf ook? Of haal je gewoon de onderdelen eruit die jou aanstaan?

Ik had namelijk de indruk dat jij niet echt 'frameworkerig' was...

Fat Pizza's pizza, they are big and they are cheezy


  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

JKVA schreef op donderdag 09 augustus 2007 @ 12:09:
[...]

Hmm, ik hoor je de laatste tijd al een paar keer over prototype. Gebruik je het framework nu zelf ook? Of haal je gewoon de onderdelen eruit die jou aanstaan?

Ik had namelijk de indruk dat jij niet echt 'frameworkerig' was...
Ik doe enkel inspiratie op uit verschillende frameworks en pas die naar eigen inzicht toe. En als bepaalde oplossingen me niet aanstaan verzin ik zelf wat anders ;)

offtopic:
Toevallig vannacht nog een blogpost gemaakt waarom het niet verstandig is volledig te vertrouwen op libraries :)

[ Voor 17% gewijzigd door crisp op 09-08-2007 14:42 ]

Intentionally left blank


  • Mei
  • Registratie: Juni 2005
  • Laatst online: 17-10-2024
Ik snap er helemaal niks van. Serieus niet. Ik zie niet wat de eerste twee functies doen of hoe ik ze moet gebruiken. Kan je me daar wat meer uitleg over geven?

En je gebruikt Prototype hiervoor. Is het ook mogelijk dit zonder het gebruik van een dikke library te doen?

  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Mei schreef op donderdag 09 augustus 2007 @ 14:52:
[...]


Ik snap er helemaal niks van. Serieus niet. Ik zie niet wat de eerste twee functies doen of hoe ik ze moet gebruiken. Kan je me daar wat meer uitleg over geven?

En je gebruikt Prototype hiervoor. Is het ook mogelijk dit zonder het gebruik van een dikke library te doen?
Je hebt enkel die eerste 2 functies nodig, niet prototype zelf (ze zijn ook herschreven door mijzelf om zo los te kunnen gebruiken).

Ik zal eens kijken of ik het uit kan leggen :)

Wat jij wilt is dat in je eventhandler 'this' naar je object instance verwijst. Je zal de uitvoering van je handler dus moeten scopen. Nu kan je dat ook zonder die helper-functies doen, dan zou je zoiets krijgen:

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Request(sMethod, sURL, bAsync, sResponseType, sCallback, aCallbackArguments, sOnError, aOnErrorArguments)
{
  // ...

  this.oHttpRequest.open(sMethod, sURL, bAsync);

  var self = this;
  var func = this.checkStatus;
  this.oHttpRequest.onreadystatechange = function()
  {
    // closure
    func.apply(self);
  }

  this.oHttpRequest.send(null);

  // ...
}

Je moet dan dus sowieso je instance-referentie in een variabele stoppen zodat je die in de closure kan gebruiken. Een beetje verbose allemaal en wanneer je ook nog met parameters wilt gaan werken die mogelijk in de onderliggende scope ook nog veranderen dan moet je alweer extra truuken gaan uithalen. bind() doet dit automatisch voor je en ondersteund ook het toevoegen van parameters:

JavaScript:
1
returnFunction = functionRef.bind(scopeObject, parm1, parm2, parm3, etcetera);


De Object.extend is eigenlijk een generieke functie om een bepaald object uit te breiden; in dit geval willen we bind() toevoegen als prototyped method aan het Function object zodat we bind als method kunnen gebruiken voor functie instances.

Intentionally left blank


  • Mei
  • Registratie: Juni 2005
  • Laatst online: 17-10-2024
crisp schreef op donderdag 09 augustus 2007 @ 15:13:
[...]

Je hebt enkel die eerste 2 functies nodig, niet prototype zelf (ze zijn ook herschreven door mijzelf om zo los te kunnen gebruiken).
Hoe kom je bij 'extend' en 'bind' (eerste codevoorbeeld)? In Google kan ik er _niks_ over vinden als standaardfuncties en van bind alleen dat veel libraries het gebruiken. Ik neem dus aan dat je bind daar als method toekent, maar de hele syntax (met de dubbele punt enzo) komt me niet bekend voor.

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Request(sMethod, sURL, bAsync, sResponseType, sCallback, aCallbackArguments, sOnError, aOnErrorArguments)
{
  // ...

  this.oHttpRequest.open(sMethod, sURL, bAsync);

  var self = this;
  var func = this.checkStatus;
  this.oHttpRequest.onreadystatechange = function()
  {
    // closure
    func.apply(self);
  }

  this.oHttpRequest.send(null);

  // ...
}
Een beetje verbose allemaal
Valt toch wel mee vergeleken met je eerste voorbeeld? :P
JavaScript:
1
returnFunction = functionRef.bind(scopeObject, parm1, parm2, parm3, etcetera);
Ik neem aan dat deze code samenwerkt met de eerste twee functies uit je eerste voorbeeld?

  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Mei schreef op donderdag 09 augustus 2007 @ 16:25:
[...]

Hoe kom je bij 'extend' en 'bind' (eerste codevoorbeeld)? In Google kan ik er _niks_ over vinden als standaardfuncties en van bind alleen dat veel libraries het gebruiken. Ik neem dus aan dat je bind daar als method toekent, maar de hele syntax (met de dubbele punt enzo) komt me niet bekend voor.
extend en bind zijn de naamgevingen uit de prototype library, het zijn geen standaardfuncties dus.
De syntax is object-notation en is verder ook gewoon suiker, verder niets.
[...]

Valt toch wel mee vergeleken met je eerste voorbeeld? :P
Niet als je dit soort dingen tientallen keren in je code moet doen ;)
[...]

Ik neem aan dat deze code samenwerkt met de eerste twee functies uit je eerste voorbeeld?
Ja, het is gewoon een voorbeeld hoe je het kan gebruiken om een handler in scope uit te voeren en er tegelijkertijd parameters aan mee te geven.

Intentionally left blank


  • Mei
  • Registratie: Juni 2005
  • Laatst online: 17-10-2024
crisp schreef op donderdag 09 augustus 2007 @ 16:33:
[...]

extend en bind zijn de naamgevingen uit de prototype library, het zijn geen standaardfuncties dus.
De syntax is object-notation en is verder ook gewoon suiker, verder niets.
Object notation, nu zie ik hem :P Wel logisch met de dubbele punt en de accolades...
[...]

Niet als je dit soort dingen tientallen keren in je code moet doen ;)
Granted ;)

Ik zit toch nog even met een paar puntjes:
1) Is dit wel IE6 compatible? Je hebt me laatste uitgelegd er IE niet geërfd wordt van Object.
2) Kan je me misschien even regel voor regel uitleggen wat die twee functies doen? Ik snap grofweg wat ze doen, maar de losse regels dringen nog niet goed tot me door.
3) Wat is nou precies het voordeel tegenover apply()? Je zei iets over variabelen die in de onderliggende scope weer veranderen enz.

  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Mei schreef op donderdag 09 augustus 2007 @ 17:05:
[...]

Ik zit toch nog even met een paar puntjes:
1) Is dit wel IE6 compatible? Je hebt me laatste uitgelegd er IE niet geërfd wordt van Object.
Dit werkt in IE5.5 en hoger. DOM objecten overerven in IE niet van Object, native javascript objecten uiteraard wel :)
2) Kan je me misschien even regel voor regel uitleggen wat die twee functies doen? Ik snap grofweg wat ze doen, maar de losse regels dringen nog niet goed tot me door.
3) Wat is nou precies het voordeel tegenover apply()? Je zei iets over variabelen die in de onderliggende scope weer veranderen enz.
Ik zal hier vanavond verder op ingaan en een voorbeeld geven bij puntje 3 :)

Intentionally left blank


  • funkwurm
  • Registratie: December 2005
  • Laatst online: 22-02-2021
crisp schreef op donderdag 09 augustus 2007 @ 19:44:
Ik zal hier vanavond verder op ingaan en een voorbeeld geven bij puntje 3 :)
Bij deze even een post puur om mijn interesse te tonen en crisp te motiveren er een mooi verhaaltje van te maken. Ik denk dat ik er ongeveer zo naar kijk als Mei qua hoever ik ben in js, maar ik vind dit wel heel interessant.

  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

ok, here goes :)

extend() is een functie bedoelt om de eigenschappen van het ene object over te kopieren naar een ander object. Let's disect :)
JavaScript:
1
Object.extend = function(dest, source, allowOverwrite)

We maken de functie aan als een eigenschap van het primitive Object object, dat heeft eigenlijk maar 1 reden: geen global scope vervuiling. Deze functie heeft 3 argumenten:
'dest' staat voor 'destination' oftwel het object waarnaartoe gekopieerd moet worden
'source' is het source object
'allowOverwrite' is een switch waarmee je kan bepalen of eventueel al bestaande eigenschappen overschreven mogen worden of niet.

Op naar de body:
JavaScript:
1
    for (var prop in source)

for-in is een taalconstructie waarmee je itereert over alle eigenschappen van een object. Uiteraard willen we alle eigenschappen weten van ons source object.
JavaScript:
1
        if (source.hasOwnProperty(prop) && (allowOverwrite || !dest.hasOwnProperty(prop)))

nu wordt het al wat lastiger. Aangezien for-in ook itereert over prototyped eigenschappen moeten we kijken of de eigenschap wel een 'eigen' eigenschap van het source object is, daar gebruik je hasOwnProperty voor. Uiteraard kijken we ook meteen even of ons doel object wellicht al over deze eigenschap beschikt wanneer allowOverwrite een boolean false oplevert.
JavaScript:
1
            dest[prop] = source[prop];

Als aan de voorwaarden voldaan is mag de eigenschap gekopieerd worden,
JavaScript:
1
    return dest;

en we retourneren uiteindelijk het uitgebreide doel object. Meestal doen we echter niets met het resultaat, objecten worden in javascript altijd by reference doorgegeven en niet by copy, dus je krijgt geen nieuw object.


Nu dat we deze handige functie hebben gemaakt moeten we 'm ook gaan gebruiken, en dat doen we om aan het Function primitive object een prototyped method toe te kennen. Pseudo-isch:
JavaScript:
1
Object.extend(Function.prototype, newProperties);

waarbij newProperties een object is. In dit geval geef ik meteen een object literal mee waarvan dit de gebruikelijke syntax is:
JavaScript:
1
2
3
4
5
{
    property1: propertyValue1,
    property2: propertyValue2,
    "#(*&$%^": propertyValue3
}

(bij speciale karakters moet de eigenschapnaam tussen quotes maar die zijn dus niet altijd verplicht)

Nu het interessante deel: de functie bind()
JavaScript:
1
    bind: function()

Dat is raar, geen argumenten? Toch wel; argumenten hoef je in javascript niet exact te benoemen, binnen de functie heb je de beschikking over de een arguments object wat een soort array-achtig object is welke de meegegeven argumenten in volgorde bevat. Kortom: je kan een wisselend aantal argumenten meegeven, en dat is leuk :)

Remember wat ik eerder zei over het gebruik van bind(): functionRef.bind(scopeObject, parm1, parm2, parm3, etcetera);
the hard part:
JavaScript:
1
        var handler = this,

We hebben bind() aangemaakt als een prototyped method van het primitive Function object. Dat wil zeggen dat elke functie instance deze method overerft. Door deze method aan te roepen op een functie instance zal bind() in de scope van die functie instance worden uitgevoerd, dus 'this' verwijst automagisch naar de functie die we willen toekennen :)
JavaScript:
1
    args = [].slice.call(arguments, 0),

auch! WTF? Wat zei ik ook alweer over het arguments object? Juist: dat het een soort array-achtig object is. Punt is dat het geen instance van Array is en we dus niet zondermeer array-methods kunnen loslaten op dat object. Da's jammer, want ik wil wel graag een echte array hebben om straks aan apply() mee te kunnen geven, en daarbij wil ik het eerste argument, mijn scopeObject, uit die array halen.

Maar waar we hier mee bezig zijn, het uitvoeren van een functie in een specifieke scope, kunnen we ook doen met native methods van een object... of een array. In het kort: ook al is arguments geen array, ik kan wel de array slice method uitvoeren op het arguments object door te zorgen dat het arguments object de scope heeft.
Dus men heeft nodig: een referentie naar de slice method hetgeen het makkelijkst is door gewoon een array literal te nemen en te verwijzen naar de slice method: [].slice
Dat is dus een functie instance, en daar kunnen we weer de call() of apply() method op loslaten waarbij het eerste argument het scopeObject (ons arguments object) is en de rest een bunch argumenten. We gebruiken call dus die argumenten kunnen komma gescheiden worden toegevoegd (bij apply moet dat een array zijn). We gebruiken maar 1 argument voor slice() namelijk het begin element, en we beginnen bij het begin dus 0.
Het uiteindelijke resultaat: de inhoud van ons arguments object maar nu in een echte array \o/
JavaScript:
1
    object = args.shift();

En nu is het dus ook kinderlijk eenvoudig om het eerste argument, ons scopeObject, uit die array te trekken: namelijk gewoon shiften :)

Nu hebben we alles wat noodzakelijk is om een functie te genereren die onze functie instance uit kan voeren in de scope van het door ons opgegeven object en met de eventuele argumenten:
JavaScript:
1
2
3
4
        return function()
        {
            return handler.apply(object, args.concat([].slice.call(arguments, 0)));
        }

Hej, waarom doe je hier weer dingen met arguments? Wel, imagine een eventhandler, wat geeft die doorgaans als argument mee aan de handler? Juist, een event object :) Die willen we dus nog toevoegen aan de uiteindelijk functiecall, dus pas ik de hele arguments-truuk nog een keer toe zodat ik uiteindelijk 2 arrays heb (de door jezelf opgegeven argumenten, en de extra argumenten zoals het event object) en die plak ik dmv concat() aan elkaar.

Uiteindelijk gebruik ik hier niet call maar apply welke de argumenten in arrayvorm accepteerd - heel handig :)


Dan nu nog een voorbeeldje met een eventhandler waarbij ik een variabel argument wil meegeven. Stel je dit voor:

JavaScript:
1
2
3
4
5
6
7
for (var i = 0, div; i < 10; i++)
{
  div = document.createElement('div');
  div.appendChild(document.createTextNode(i));
  div.onclick = function() { alert(i); }
  document.body.appendChild(div);
}

Je krijgt nu 10 divjes, maar op welke ik ook klik zet alerten allemaal '10'. Dat komt omdat de variabele i in de anonieme functie een referentie is naar de i in de onderliggende scope, en die staat na het completeren van de loop op 10. Het voordeel van bind() is dat het een extra scope genereerd, dus ik kan eenvoudig dit doen:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
function handler(i)
{
    alert(i);
}

for (var i = 0, div; i < 10; i++)
{
  div = document.createElement('div');
  div.appendChild(document.createTextNode(i));
  div.onclick = handler.bind(div, i);
  document.body.appendChild(div);
}

Intentionally left blank


  • Mei
  • Registratie: Juni 2005
  • Laatst online: 17-10-2024
Wow, heel verhaal :D Ik begin nu te snappen wat dit allemaal doet. Het totaalplaatje is met mijn kennis nog even lastig te overzien, maar als ik aan de hand van jouw uitleg het doorneem, dan is het duidelijk genoeg om te snappen hoe je het voor elkaar hebt gekregen. Bedankt!
crisp schreef op donderdag 09 augustus 2007 @ 22:43:
JavaScript:
1
2
3
4
5
6
7
for (var i = 0, div; i < 10; i++)
{
  div = document.createElement('div');
  div.appendChild(document.createTextNode(i));
  div.onclick = function() { alert(i); }
  document.body.appendChild(div);
}

Je krijgt nu 10 divjes, maar op welke ik ook klik zet alerten allemaal '10'. Dat komt omdat de variabele i in de anonieme functie een referentie is naar de i in de onderliggende scope, en die staat na het completeren van de loop op 10. Het voordeel van bind() is dat het een extra scope genereerd, dus ik kan eenvoudig dit doen:
Ik volg alleen nog even niet waarom je telkens '10' krijgt. Je hebt de scope van de for-loop met daarin i. Vervolgens ga je i in een nieuwe scope in die loop gebruiken. Dan zou je (ik) óf verwachten dat i er niet is (nieuwe scope) óf dat i de waarde heeft die hij in de loop heeft op dat moment, namelijk een waarde vanaf 0 t/m 9.

Nog één vraagje: mag ik dit script voor een commercieel project gebruiken?

Verwijderd

JavaScript blijft toch een coole taal. Don't care what the haters say.

JavaScript:
1
    args = [].slice.call(arguments, 0),

Jeez, dat ik daar nooit opgekomen ben. Of dat het me nooit opgevallen is in de prototype lib. :D Die gaat nog heel goed van pas komen.

  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Mei schreef op vrijdag 10 augustus 2007 @ 07:45:
Ik volg alleen nog even niet waarom je telkens '10' krijgt. Je hebt de scope van de for-loop met daarin i. Vervolgens ga je i in een nieuwe scope in die loop gebruiken. Dan zou je (ik) óf verwachten dat i er niet is (nieuwe scope) óf dat i de waarde heeft die hij in de loop heeft op dat moment, namelijk een waarde vanaf 0 t/m 9.
Ten eerste creëerd een for-loop in javascript geen nieuwe scope, en verder is dit een kwestie van scope-resolution (dit specifieke geval noem je een closure). Op het moment dat de handler wordt uitgevoerd zal deze de variabele i proberen te resolven. Die is niet gedefinieerd in de handler functie zelf, dus zal de scope-chain afgegaan worden. De variabele i in de onderliggende scope heeft echter op dat moment de waarde 10.
Op het moment dat je de variabele i doorgeeft aan een andere functie als argument dan genereer je wel een nieuwe scope waarin het argument is gedeclareerd.
Nog één vraagje: mag ik dit script voor een commercieel project gebruiken?
Aangezien dit afgeleide functies van prototype zijn, welke een MIT license heeft, is dat geen probleem :)

Intentionally left blank


  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Verwijderd schreef op vrijdag 10 augustus 2007 @ 08:52:
[...]

JavaScript blijft toch een coole taal. Don't care what the haters say.

JavaScript:
1
    args = [].slice.call(arguments, 0),

Jeez, dat ik daar nooit opgekomen ben. Of dat het me nooit opgevallen is in de prototype lib. :D Die gaat nog heel goed van pas komen.
Dat komt ook niet uit prototype ;) Ik geloof dat ik het John Resig voor het eerst heb zien doen in JQuery.

En ja, het is reuze handig. Zo heb ik bijvoorbeeld de javascript 1.6 forEach samen met de generic daarvoor gebackport gebruikmakend van een dergelijke constructie:

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Object.extend(Array.prototype,
{
    forEach: function(func /*, thisp*/)
    {
        if (typeof func != 'function')
            throw new TypeError();

        for (var i = 0, len = this.length, thisp = arguments[1]; i < len; i++)
        {
            if (i in this)
                func.call(thisp, this[i], i, this);
        }
    }
});

if (!('forEach' in Array))
{
    Array.forEach = function(array)
    {
        [].forEach.apply(array, [].slice.call(arguments, 1));
    }
}


En nu vraag je je af wat je hiermee nou kan doen? Wel, met de generic kunnen we makkelijk forEach loslaten op array-like structuren zoals bijvoorbeeld een nodeList:

JavaScript:
1
Array.forEach(document.getElementsByTagName('a'), someFunction);

:)

Intentionally left blank


Verwijderd

JavaScript:
1
forEach: function(func /*, thisp*/)

Grappige notatie voor optionele parameters. Zien we hier trouwens een bugje in de highlighter/parser?

  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Verwijderd schreef op vrijdag 10 augustus 2007 @ 11:44:
JavaScript:
1
forEach: function(func /*, thisp*/)

Grappige notatie voor optionele parameters. Zien we hier trouwens een bugje in de highlighter/parser?
hmmz, inderdaad een bugje :)

Intentionally left blank


  • Mei
  • Registratie: Juni 2005
  • Laatst online: 17-10-2024
crisp schreef op vrijdag 10 augustus 2007 @ 11:23:
[...]

Ten eerste creëerd een for-loop in javascript geen nieuwe scope
Ik heb het misschien een beetje ongelukkig neergezet, maar ik bedoelde de scope waarin de for-loop zat, niet dat de loop een eigen scope maakt.
Die is niet gedefinieerd in de handler functie zelf, dus zal de scope-chain afgegaan worden. De variabele i in de onderliggende scope heeft echter op dat moment de waarde 10.
Wat is de onderliggende scope precies? De scope waarin die functie is gedefiniëerd/aangeroepen?
Aangezien dit afgeleide functies van prototype zijn, welke een MIT license heeft, is dat geen probleem :)
Het ging mij er om dat ik jouw code gewoon 1:1 wilde gebruiken. Lijkt me dan wel net zo netjes het even te vragen als je zoiets doet :) Toch bedankt hiervoor, ga straks eens kijken of ik de boel aan de praat krijg. Verder ook bedankt voor de goede uitleg ;) Je hebt me toch weer een aantal dingen duidelijk gemaakt.

  • JKVA
  • Registratie: Januari 2004
  • Niet online

JKVA

Design-by-buzzword fanatic

Mei schreef op vrijdag 10 augustus 2007 @ 13:35:
[...]
Ik heb het misschien een beetje ongelukkig neergezet, maar ik bedoelde de scope waarin de for-loop zat, niet dat de loop een eigen scope maakt.
Wat er in feite gebeurt bij deze manier van coderen, is dat er een closure gemaakt wordt. Een closure houdt heel bot gezegd in dat alle variabelen in dezelfde scope als je function call in leven blijven, zelfs nadat de function is afgerond. Ofwel, de variabele i staat nog steeds op 10 op het moment dat jij klikt (of je moet met lichtsnelheid klikken terwijl de for loop nog draait).

Ofwel, bij een closure blijven variabelen met function scope zelfs in leven nadat de function afgerond is. Dat is tegelijk een voordeel en een nadeel van closures. Het behoud van state kan gemakkelijk zijn, maar tegelijk een memory leak of gewoon functioneel fout.

Fat Pizza's pizza, they are big and they are cheezy


  • Mei
  • Registratie: Juni 2005
  • Laatst online: 17-10-2024
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
38
39
40
41
42
43
44
45
Object.extend = function(dest, source, allowOverwrite)
{
    for (var prop in source)
    {
        if (source.hasOwnProperty(prop) && (allowOverwrite || !dest.hasOwnProperty(prop)))
            dest[prop] = source[prop];
    }

    return dest;
}

Object.extend(Function.prototype,
{
    bind: function()
    {
        var handler = this, args = [].slice.call(arguments, 0), object = args.shift();

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

//Bovenstaande letterlijk overgenomen

function Request(sMethod, sURL, bAsync, sResponseType, sCallback, aCallbackArguments, sOnError, aOnErrorArguments)
{
    setStatus('loading', 'progress');
    if (window.XMLHttpRequest)
    {
        this.oHttpRequest=new XMLHttpRequest();
    }
    else if (window.ActiveXObject)
    {
        this.oHttpRequest=new ActiveXObject("Microsoft.XMLHTTP");
    }
    this.oHttpRequest.onreadystatechange=this.checkStatus.bind(this);
    this.oHttpRequest.open(sMethod, sURL, bAsync);
    this.oHttpRequest.send(null);

if(typeof Request._initialized=='undefined')
    {
        Request.prototype.checkStatus=function()
        {


Ik krijg de volgende error: "this.checkStatus has no properties.". Ik zou zeggen dat jouw code de bind() method niet goed doorgeeft aan functies aan de hand van deze melding, maar als ik dan (met mijn beperkte kennis) naar jouw code kijk, dan zie ik nergens iets verkeerds.

  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Waarom wil je de checkStatus method conditioneel toevoegen, en waarom doe je dat binnen de constructor van Request()?

Note ook dat je beter de onreadystate handler na open() kan toevoegen.

Intentionally left blank


  • Mei
  • Registratie: Juni 2005
  • Laatst online: 17-10-2024
Niet conditioneel. Dat if-statement zorgt ervoor dat die method maar één keer aan Request objecten geprototyped worden. Aan het einde van de constructor staat namelijk Request._initialized=true;. Waarom het binnen de constructor gebeurt is om het overzichtelijk te houden (geleerd Nicholas C. Zakas, maar ik vind er zelf inderdaad wel wat in zitten om het niet buiten de constructor te doen.

  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Mei schreef op maandag 13 augustus 2007 @ 12:58:
Niet conditioneel. Dat if-statement zorgt ervoor dat die method maar één keer aan Request objecten geprototyped worden. Aan het einde van de constructor staat namelijk Request._initialized=true;. Waarom het binnen de constructor gebeurt is om het overzichtelijk te houden (geleerd Nicholas C. Zakas, maar ik vind er zelf inderdaad wel wat in zitten om het niet buiten de constructor te doen.
Dan snap je het OO-principe niet ben ik bang ;)

Normaliter zou je dit doen:
JavaScript:
1
2
3
4
5
6
7
8
9
function Request()
{
}
Request.prototype.checkStatus = function()
{
}

var request1 = new Request();
var request2 = new Request();


of wil je meer een singleton principe?

Intentionally left blank


  • Mei
  • Registratie: Juni 2005
  • Laatst online: 17-10-2024
Snap ik, maar als je gewend bent met classes te werken waarin je methods en properties definiëert is het heer onhandig de methods buiten de class te definiëren. Het wordt er niet overzichtelijker op, vind ik zelf. Ik heb hier voor het eerst over gelezen in het boek van Zakas, maar ik vond wel dat hij gelijk had. Hij legde uit dat je het kon doen op de manier zoals in jouw voorbeeld, maar ook op mijn manier.

  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Mei schreef op maandag 13 augustus 2007 @ 13:25:
Snap ik, maar als je gewend bent met classes te werken waarin je methods en properties definiëert is het heer onhandig de methods buiten de class te definiëren. Het wordt er niet overzichtelijker op, vind ik zelf. Ik heb hier voor het eerst over gelezen in het boek van Zakas, maar ik vond wel dat hij gelijk had. Hij legde uit dat je het kon doen op de manier zoals in jouw voorbeeld, maar ook op mijn manier.
Deze aanpak heeft 2 nadelen:

Ten eerste (en waar jij nu tegenaan loopt) dat je geen prototyped methods kan gebruiken in je constructor voordat je ze gedefinieerd hebt (logisch toch?)
Ten tweede dat je een onnodige extra scope introduceert voor je prototyped methods

Intentionally left blank


  • Mei
  • Registratie: Juni 2005
  • Laatst online: 17-10-2024
Crisp, ik heb zo'n vermoeden dat er ergens een bugje in jouw code zit. Als ik this.checkStatus.call(this) doe, dan wordt checkStatus netjes aangeroepen, maar bij this.checkStatus.bind(this) niet.

  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Mei schreef op woensdag 15 augustus 2007 @ 12:23:
Crisp, ik heb zo'n vermoeden dat er ergens een bugje in jouw code zit. Als ik this.checkStatus.call(this) doe, dan wordt checkStatus netjes aangeroepen, maar bij this.checkStatus.bind(this) niet.
De eerste is ook een directe functie-call terwijl de tweede een functie-reference teruggeeft. Heb je de toewijzing aan je readystatechange eventhandler nou al verplaatst?

Intentionally left blank


  • Mei
  • Registratie: Juni 2005
  • Laatst online: 17-10-2024
En hoe roep je een functie aan de hand van een reference dan aan, zegmaar?

Huidige code:
JavaScript:
1
2
3
    this.oHttpRequest.open(sMethod, sURL, bAsync);
    this.oHttpRequest.onreadystatechange=this.checkStatus.bind(this.oHttpRequest, sResponseType, sCallback, aCallbackArguments, sOnError, aOnErrorArguments);
    this.oHttpRequest.send(null);

Had toenstraks de eventhandlerdeclaratie al verplaatst. Gebruikte net nog call() ipv bind() en toen werkt checkStatus() slechts bij de eerste readystatechange aangeroepen. Met bind wordt hij nu 4 keer aangeroepen. Ben nog niet zover dat ik uit mezelf snap waarom het nou wel/niet werkte, maar dit probleem is dus in ieder geval opgelost :)

  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Als je nu in plaats van die hele sloot argumenten enkel een reference naar je Request instance meegeeft; het zijn immers allemaal properties daarvan...

Intentionally left blank


  • Mei
  • Registratie: Juni 2005
  • Laatst online: 17-10-2024
Die argumenten worden doorgegeven aan de Request instance en er wordt alleen in checkStatus() wat mee gedaan. Ik was eerst een andere opzet van plan, vandaar dat ik er allemaal properties van had gemaakt. Dat ging toch niet door, dus vandaar dat ik ze zo doorgeef.

  • XWB
  • Registratie: Januari 2002
  • Niet online

XWB

Devver
crisp schreef op donderdag 09 augustus 2007 @ 22:43:
ok, here goes :)

crisps l33t uitleg
Super handig deze manier van werken. Maar hier ben ik nog niet uit:

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
function karlTest ( number )
{
    this.id = number;
    
    this.init();
}

Object.extend ( karlTest.prototype,
{
    init: function ()
    {
        var div = document.createElement ( 'div' );
        
        div.onmouseover = this.blaat;
    },
    blaat: function ()
    {
        alert ( this );
        
        this.testt ();
    },
    testt: function ()
    {
        alert ( 'aangeroepen' );
    }
});

new karlTest ( 5 );


Aangezien this binnen blaat() nu naar het div element verwijst bestaat this.testt () uiteraard niet. Hoe kan ik toch blaat() aanroepen?

[ Voor 55% gewijzigd door XWB op 31-12-2007 01:15 ]

March of the Eagles


  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

JavaScript:
1
div.onmouseover = this.blaat.bind(this);

;)

Intentionally left blank


  • XWB
  • Registratie: Januari 2002
  • Niet online

XWB

Devver
Ik had het moeten proberen. Dank je :)

March of the Eagles


  • XWB
  • Registratie: Januari 2002
  • Niet online

XWB

Devver
Hm, ik mis nog iets:

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
function karlTest ( number )
{
    this.id = number;
    
    this.init();
}

Object.extend ( karlTest.prototype,
{
    init: function ()
    {
        var div = document.getElementById ( 'div' );
        
        div.onmouseover = this.blaat.bind ( this );
    },
    blaat: function ()
    {
        alert ( this.parentNode ); // undefined
        
        this.testt ();
    },
    testt: function ()
    {
        alert ( 'aangeroepen' );
    }
});

new karlTest ( 5 );


/edit, gevonden, even logisch nadenken is altijd handig:

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
function karlTest ( number )
{
    this.id = number;
    
    this.init();
}

Object.extend ( karlTest.prototype,
{
    init: function ()
    {
        var div = document.getElementById ( 'div' );
        
        div.onmouseover = this.blaat.bind ( this, div );
    },
    blaat: function ( el )
    {
        alert ( el.parentNode ); // htmlBody
        
        this.testt ();
    },
    testt: function ()
    {
        alert ( 'aangeroepen' );
    }
});

new karlTest ( 5 );


Ik snap hem, denk ik ;)

[ Voor 36% gewijzigd door XWB op 31-12-2007 02:05 ]

March of the Eagles

Pagina: 1