Hoe werkt de scope chain?

Status
Niet open voor verdere reacties.

antonwas

Gebruiker
Lid geworden
11 nov 2006
Berichten
254
Beste lezers,

Ik ben bezig om Object georiënteerd Javascript te leren. Ik gebruik daarvoor Object Oriented JavaScript i.c.m. wat er zoal op internet te vinden is. Nu ben ik aan het stoeien met de scopechain en closures. Op internet kwam ik de volgende uitleg tegen:
http://http://javascript.info/tutorial/closures

In het kader van de scopechain worden er twee voorbeeldjes genoemd. Ze worden in de uitleg als 'redelijk vanzelfsprekend beschouwd' Toch begrijp ik er weinig van. Het eerste voorbeeld:

Code:
function sum(a) {
   return function(b) { // takes a from outer LexicalEnvironment
    return a+b
  } 
}
 
alert( sum(1)(2) )

Hier zit ik met het volgende probleem:
Voor zover ik weet wordt een return-waarde (in dit geval een anonieme functie) altijd teruggegeven aan het aanroepende statement. In dit geval is het aanroepende statement de halve parameter (sum(1)) van de functie Alert(). Het tweede deel van de Alert-parameter (2) is de input voor de binnenste functie. De vraag is: hoe weet de interpreter dat deze (2) dient als aanroep (en input) voor de binnenste functie?

Het tweede voorbeeld is een uitbreiding op het eerste:

Code:
function sum(a) {
   
  var sum = a
   
  function f(b) {
    sum += b
    return f
  }
   
  f.toString = function() { return sum }
   
  return f
}
 
alert( sum(1)(2) )  // 3
alert( sum(5)(-1)(2) )  // 6
alert( sum(6)(-1)(-2)(-3) )  // 0
alert( sum(0)(1)(2)(3)(4)(5) )  // 15

Van dit tweede voorbeeld begrijp ik helemaal niets meer. Zou iemand deze functie uit kunnen leggen? inclusief scopechain-principe en waarom het in vredesnaam mogelijk is om uiteindelijk zoveel getallen bij elkaar op te tellen als je zelf wilt?

Groet,

Anton
 
Ah, de dark side of Javascript :D


**noot: wat hier uitgelegd staat is niet (helemaal) waar. Het idee is belangrijk maar omdat JS zo'n speciaal taaltje is werkt het allemaal net even wat anders. Voor een meer kloppende uitleg, zie hier: http://bonsaiden.github.com/JavaScript-Garden/#function.scopes


hoe weet de interpreter dat deze (2) dient als aanroep (en input) voor de binnenste functie?

Ik had eerst een heel stuk text getyped, maar ik denk dat het daar alleen maar minder duidelijk van wordt. Dus hier een kleinere, minder vage uitleg:
edit: nevermind, het is alsnog veelste lang geworden. Lees ze.




Aleereerst: als in een bepaalde scope een var niet 'gevonden' wordt, zal 't gaan kijken naar de 'hogere' (of eigenlijk lagere, het is maar hoe je het bekijkt) scopes. Even als voorbeeld ({ en } staan voor een scope):

[JS]{
var a;
{
var b;
a; // <-- niet in deze scope, dus kijken we eentje hoger, en daar bestaat 'ie wel.
}
}[/JS]

Dit gebeurt overal; als je (in je browser) ergens bijvoorbeeld alert() aanroept, klaagt 'ie niet dat ie deze niet kent: hij gaat 'zoeken', en vind uiteindelijk in window (de global) een alert() functie.

Om dit punt aan te tonen kan je een klein testje doen:

[JS]function sauce()
{
var alert = function(x)
{
console.log('fake alert: ', x);
};

alert('bbq'); // dit geeft geen alert-popup, maar een entry in de console, want: kijk eerst in eigen scope, dan die erboven
}[/JS]
[JS]function moar()
{
alert('bbq'); // dit geeft WEL een alert-popup, want: niet in eigen scope, maar wel in die erboven (global)
}[/JS]

In het 1e stukje zoekt 'ie in de huidige scope. Daar vind 'ie een alert functie, dus gebruikt 'ie die. Als deze niet gevonden wordt (2e stukje) zal 'ie verder zoeken, in dit geval buiten de functie, en vind ie de globale alert functie.


Nu, terug naar je vraag:

Op het moment dat sum() wordt uitgevoerd, weten we wat de parameter 'a' is. Dit is, in je voorbeeld, 1.


Dan gaan we verder. We zien een return statement, ofwel, wat erna komt is de value die we terug gaan geven *aan de caller*. Als hier had gestaan return a; hadden we simpelweg de value (again, in je voorbeeld 1) terug gegeven. Maar neeeeeeeuuu, we moeten weer eens fancy doen en een functie terug geven.

Het key point hier is dat we een functie returnen. We executen de functie zelf niet, maar hij wordt wel geparsed als we sum aanroepen. Waarom? Stel we hadden deze:

[JS]function sum(x)
{
return x * 2;
}[/JS]

als we sum aanroepen, returnen we iets. Hetgene wat we returnen is een expression. Deze kan eerst berekend moeten worden, zoals hier; we returnen twee keer de invoer. Bij een function return wordt de function ook eerst geparsed.

Nu, deze functie heeft 1 parameter, 'b', dus die negeren we for now (want: deze is dus 'dynamisch'). De a, echter, is geen parameter. Echter, deze bestaat niet in de scope van de return-function! Dus: een scope'je hoger, en daar hebben we net a gemaakt: deze is 1. Even voor de duidelijkheid:

[JS]function sum(a) {
// a bekend, is meegegeven als parameter. Stiekem staat hier dus zoiets:
var a = ...;

return function(b) {
// b 'bekend', is parameter
// a is hier onbekend
return a+b
}
}[/JS]


Om het dus samen te vatten (ik ga eten): sum wordt uitgevoerd, return waardes worden berekend, oh noes een functie -> parse deze, en vul waardes in.


Nog een tip, dit soort dingetjes zijn heel leuk enzo maar je gebruikt ze 9 van de 10 keer toch nooit en zijn eigenlijk alleen maar om te laten zien dat je de rare kronkelingen in een taal begrijpt. Leuk, maar niet echt, well, super nuttig.

Also, het helpt om dingen uit te schrijven. In jou voorbeeld:

[JS]function sum(a)
{
return function(b)
{
return a+b
}
}

/////////////////


var somMet1 = sum(1); // == function(b){return 1 + b; }

var drie = somMet1(2);

alert(drie); // '3'[/JS]




:thumb:
 
Laatst bewerkt:
Status
Niet open voor verdere reacties.
Terug
Bovenaan Onderaan