33 funzioni espressioni 2 – Scoping

Vediamo un altro caso in cui le funzioni espressione sono davvero utili.

Immaginiamo di programmare un gioco e chiedo la creazione di un nemico demandata alla funzione scegli_avversario()

var primo_nemico = scegli_avversario();
primo_nemico.comportamento();

ogni nemico ha un suo comportamento preso dalla chiamata primo_nemico.comportamento, tralasciamo il fatto che è un oggetto, ma concentriamoci sul fatto che il comportamento e il risultato non è fisso, ma varia a seconda della restituzione, chiamato da scegli_avversario() e agganciato alla property comportamento, restituito da una funzione espressione che lo estrae a sorte, ma vediamo queste funzioni espressione:

//funzioni come parametri inviati ad altre funzioni
var comportamento_docile = 
function()
{writeln("beehee beehee ...");}

var comportamento_deciso = 
function()
{writeln("bau! bau!...");}

var comportamento_aggressivo = 
function()
{writeln("GROARR! ...");}

Ciascuna di esse definisce un comportamento (docile, deciso, aggressivo).

La funzione scegli_avversario() estrae casualmente un comportamento:

function scegli_avversario()
{
  switch( Math.floor(Math.random() * 3) + 1 )
  {
    case 1:
      return crea_nemico( comportamento_docile );
    break;

    case 2:
      return crea_nemico( comportamento_deciso );
    break;

    case 3:
      return crea_nemico( comportamento_aggressivo );
    break;
  }
}

Math.random ritorna un numero casuale tra 0 e 1 che viene moltiplicato per 3 (arrivando ad un massimo di 2,99999999…∞) aggiunge 1 e toglie la parte decimale con Math.floor avendo come numero massimo 3.

nei vari case viene restituita la funzione crea_nemico ma viene passato come parametro una funzione.

case 1:
return crea_nemico( comportamento_docile );
break;

ecco quindi la funzione crea_nemico:

function crea_nemico( comportamento )
{
  //crea un oggetto 'nemico' associandogli
  //il comportamento scelto a caso

  var nemico = {}; //oggetto vuoto

  nemico.classe = "dragone";
  nemico.salute = 100;
  nemico.comportamento = comportamento;
  return nemico;
}

In questa funzione oltre ad aver creato un oggetto (specificheremo più avanti)

var nemico = {};

stiamo aggiungendo delle proprietà

nemico.classe = "dragone";
nemico.salute = 100;
nemico.comportamento = comportamento;
return nemico;

La proprietà nemico.comportamento riceve dal parametro comportamento il valore dato dalla funzione passata come parametro ed estratta casualmente, per es:

 crea_nemico (comportamento_docile)

Costruito il nemico lo restituisco

return nemico;

Lo andiamo a memorizzare nella variabile primo_nemico e richiamo il suo comportamento

var primo_nemico = scegli_avversario();
primo_nemico.comportamento();

Come regola finale di dove si può utilizzare una funzione espressione diciamo che si può utilizzare ovunque sia previsto che possa essere utilizzata un’espressione.

Visibilità variabili (scoping)

Adesso che i nostri script possono potenzialmente essere maggiormente complessi, è molto importante riconoscere per ogni identificatore la sua visibilità, ovvero riconoscere quale sezione del codice può fare riferimento a quell’identificatore senza generare conflitti con altri che potrebbero avere lo stesso nome.
Lo ‘scope‘ di una variabile o visibilita’ di una variabile, è la sezione di codice in cui essa e’ visibile (in scope) ed in Javascript e’ regolato con l’uso
delle funzioni.

Non bisogna confondere lo scope con il context (contesto) che indica invece l’oggetto per il quale una funzione viene eseguita.
Per una funzione visibile a livello globale cioe’ non contenuta in alcun oggetto questo in realtà esiste e coincide con l’oggetto window .

La distinzione e’ tra visibilità globale, locale e di blocco.

VISIBILITA’ GLOBALE (global scope)

E’ quella che ha come riferimento l’intero codice. Sono le variabili/oggetti
dichiarate all’esterno di qualunque funzione o (orrore!) usate in una
funzione SENZA il modificatore var (o let).
Gold roule: in generale la visibilità globale e’ DA EVITARE. es di uso cattivo:

var x = 100;

function f_bad() {
  //modifica la variabile esterna potenzialmente all'insaputa di chi la chiama
  x = 2; 
}

js ci permette di modificare il valore della variable globale x

writeln("Prima di invocare f_bad() " + x);
f_bad();  //per quale motivo dovremmo aspettarci che modifichi x ??
writeln("Dopo f_bad() " + x); //eppure ... EVITARE!

Invocando f_bad la x che valeva 100 varrà 2, pensate a qualcuno che la invoca in migliaia di righe di codice.

VISIBILITA’ LOCALE (function scope)

E’ quella che ha come riferimento il codice di una funzione e delle
altre eventuali funzioni definite all’interno di questa. Riprendendo l’esempio di prima, vediamo l’uso corretto tramite l’identificatore var

x = 100;
//versione corretta di f
function f_good() {
  //modifica una variabile LOCALE distinta da quella globale
  var x = 2; 
  var y=200;
}

La x interna alla funzione, NON è quella esterna, dopo aver richiamato f_good() la x esterna varrà sempre 100 e il suo valore non verrà modificato

writeln("Prima di invocare f_good() " + x);
f_good();  //per quale motivo dovremmo aspettarci che modifichi x ??
writeln("Dopo f_good() " + x); //OK
//writeln(y); errore: y not defined; visibile solo a f_good in esecuzione

La x vale 100, dopo f_good() vale sempre 100. Le variabili incapsulate dentro la funzione con var o let non possono essere viste fuori dal contesto di quella funzione e sono variabili locali.