Il logo del sito

Appunti su JavaScript

Funzioni Java Script

§ 1. Funzioni

Sulle funzioni JavaScript pensavo che non ci fosse molto da dire sino a che, studiando l'interfaccia fileReader della API FILE, non mi sono imbattuto in questo pezzo di codice:


reader.onload=(function(theFile){
   return function(e){
      var span = document.createElement('span');
      span.innerHTML = ['<img class="thumb" src="', e.target.result,
         '" title="', escape(theFile.name), '"/>'].join('');
      document.getElementById('list1').insertBefore(span, null);
   };
})(f);
reader.readAsDataURL(f);
            

Questo pezzo di codice mi ha messo a terra. Non ci ho capito niente. Ho cominciato a cercare sul web e ho capito che sulle funzioni c'è da dire molto di più di quanto presentato in w3schools.com/js/js_functions.asp

In particolare ho trovato due libri su JavaScript in cui i capitoli sulle funzioni sono molto più completi del tutorial di w3schools. I due libri sono:

L'esame superficiale dei due libri non mi è stato però sufficiente a capire il pezzetto di codice di cui sopra. Ho deciso di leggere accuratamente i capitoli dedicati alle funzioni dei due libri eseguendo tutti gli esercizi.

§ 2. Eloquent JavaScript - Functions

Una funzione è creata per mezzo di una espressione che inizia con la parola chiave function. Le funzioni hanno un insieme di parametri ed un body, il quale contiene gli statements che saranno eseguiti quando la funzione viene chiamata. Il body della funzione è sempre racchiusa fra graffe anche se consiste in un solo statement.


var square = function(x){
   return x*x;
};
            

Una funzione può avere più parametri o non averne affatto.


var makeNoise = function(){
   console.log('Pling');
};


var power = function(base, exponent){
  var result = 1;
  for (var count = 0; count < exponent; count++)
    result *= base;
  return result;
};

makeNoise() non ne ha nessuno, power() ne ha due.

Alcune funzioni come power e square restituiscono un valore. Altre, come makeNoise, non restituiscono alcun valore.

Il valore restituito da una funzione è quello dell'espressione che segue la keyword return.

La keyword return non seguita da alcuna istruzione restituisce un valore undefined.

§ 2.1 Eloquent JavaScript - Parametri ed ambiti

I parametri di una funzione si comportano come variabili regolari, ma i loro valori iniziali sono dati da chi chiama la funzione.

Una importante proprietà delle funzioni è che le variabili dichiarate al loro interno, compresi i parametri, sono variabili. Questo significa, per esempio, che la varibile result della funzione power sopra illustrata, sarà creata ad ogni chiamata della funzione.

Questa "localizzazione" delle variabili si applica solo ai parametri, e alle variabili dichiarate con la keyword var all'interno del body della funzione. Le variabili dichiarate all'esterno di una qualsiasi funzione sono globali. E' possibile accedere a queste funzioni all'interno di una funzione a meno che non venga dichiarata, all'interno della funzione stessa, una varibile con lo stesso nome.

Il codice che segue mostra questo comportamento.


var xString = "Variabile globale xString, dichiarata esternamente da ogni funzione.";

var varLoc = function() {
  var xString = "Variabile locale xString dichiarata internamente a varLoc()";
};
varLoc();
console.log(xString);
// → Variabile dichiarata esternamente

var varGlob = function() {
  xString = "Variabile globale xString, dichiarata esternamente, e modificata da varGlob()";
};
varGlob();
console.log(xString);
// → variabile dichiarata internamente a varGlob

§ 2.2 Eloquent JavaScript - Ambiti nidificati

Ecco confermata l'importanza dello studio approfondito. Credo che dopo aver capito questo paragrafo, gran parte del problema iniziale sarà risolto.

JavaScript non solo distingue fra variabili locali e globali ma, permettendo di creare funzioni all'interno di altre funzioni, produce diversi gradi di localizzazione.

Ad esempio, questa funzione che è piuttosto priva di significato, ha all'interno altre due funzioni:


var paesaggio = function() {
  var result = "";
  var pianura = function(size) {
    for (var count = 0; count < size; count++)
      result += "_";
  };
  var montagna = function(size) {
    result += "/";
    pianura(1);
    for (var count = 0; count < size; count++)
      result += "'";
    result += "\\";
  };

  pianura(3);
  nontagna(4);
  pianura(6);
  montagna(1);
  pianura(1);
  return result;
};

console.log(paesaggio());
// → ___/''''\______/'\_

In effetti questa funzione è piuttosto priva di significato, ma vediamo dove si vuol andare a parare.

Le funzioni montagna e pianura possono vedere la variabile result, dal momento che esse sono interne alla funzione che definisce result. Ma esse pianura non può vedere la variabile count definita in montagna e viceversa montagna non può vedere la variabile count definita in pianura. L'ambiente esterno alla funzione paesaggio non non può vedere nessuna delle variabili definite all'interno di paesaggio.

In breve, ogni ambito locale può vedere l'ambito che lo contiene. L'insieme delle variabili visibili all'interno di una funzione è determinato dal posto in cui è collocata la funzione nel testo del programma.

Da una funzione, sono visibili tutte le variabili dei blocchi che circondano la definizione della funzione, ossia quelle dei corpi delle funzioni che la racchiudono e quelle esterne a tutte le funzioni.

§ 2.3 Eloquent JavaScript - Dichiarazione di funzione

C'è un metodo leggermente più breve per dire "var square = function...". La parola chiave function può essere usata all'inizion di uno statement com segue:


function square(x){
   return x*x;
}

Questa è una dichiarazione di funzione. Lo statement definisce la variabile square e punta alla funzione data. Comunque c'è una sottigliezza con questa forma di definizione di funzione:

There is one subtlety with this form of function definition, however.

console.log("Il futuro dice: ", future());
function future(){
   return "Non abbiamo ANCORA auto volanti;
}

Questo codice funziona sebbene la funzione sia definita dopo il codice che la usa. Questo perché le dichiarazioni di funzione non sono parte del normale flusso top-down del controllo. Esse sono concettualmente spostate in cima al loro contesto. Questo a volte è utile perché di da la libertà di ordinare il codice in una maniera che ci sembri significativa.

§ 2.4 Eloquent JavaScript - Call stack

Concetto che già possiedo.

§ 2.5 Eloquent JavaScript - Optional Parameter

JavaScript è di vedute piuttosto larghe riguardo al numero di argomenti che possono essere passati ad una funzione. Se vengono passati troppi argomenti, quelli in eccesso sono ignorati. Se vengono passati pochi argomenti, a quelli che mancano viene assegnato il valore undefined.

Questo aspetto offre dei pro e dei contro.

Il contro à che non si viene avvertiti se si passano meno argomenti del necessario.

Il pro è che si possono realizzare funzioni con un numero variabile di argomenti.

§ 2.5 Eloquent JavaScript - Closure

La capacità di trattare funzioni come valori, combinata con il fatto che le variabili locali sono ri-create ad ogni chiamata della funzione, conduce ad un interessante quesito: Cosa succede alle variabili locali quando la chiamata alla funzione che le ha create non è più attiva?

Il codice che segue mostra un esempio di questo. Esso definisce una funzione, wrapValue, che crea un variabile locale. Essa poi restituisce una funzione che accede e restituisce questa variabile locale


function wrapValue(n) {
  var localVariable = n;
  return function() { return localVariable; };
}

var wrap1 = wrapValue(1);
var wrap2 = wrapValue(2);
console.log(wrap1());
// → 1
console.log(wrap2());
// → 2

Questo è permesso e funziona come sperato. La localVariable può ancora essere acceduta. Più istanze di localVariable sono vive contemporaneamente, il chè è un'altra buona illustrazione del concetto che le variabili locali sono veramente ri-create ad ogni chiamata.

Questa funzionalità, consistente nella capacità di referenziare una specifica istanza di una variabile locale in una funzione in fase di chiusura, è chiamata closure. Una funzione che chiude su qualche variabile locale è chiamata una chiusura. Questo comportamento non solo libera dalla preoccupazione riguardo ai tempi di vita delle variabili, ma permette qualche uso creativo delle funzioni.

Con un leggero cambiamento, possiamo trasformare l'esempio precedente in un modo per creare funzioni che si moltiplicano per una quantità arbitraria


function multiplier(factor) {
  return function(number) {
    return number * factor;
  };
}

var twice = multiplier(2);
console.log(twice(5));
// → 10

Pensare programmi come questo richiede pratica. Un buon modello mentale, è quello di pensare alla parola chiave function come ad un congelatore del codice nel suo body e al suo pakaging. Quando leggi return function(){} pensa ad esso come la restituzione di un handler congelato pronto per un successivo uso.

Nell'esempio la funzione multiplier restituisce un pezzo di codice congelato indirizzato dalla variabile twice. twice(number) determina la esecuzione del codice congelato che ancora conserva il valore di factor

§ 3. JavaScript Basiscs by Rebecca Murphey - Self-Executing Anonymous Functions

Sembrerebbe questo il pezzetto che manca.

Provo a tradurre questo argomento dall'articolo di Rebecca Murphey.

Uno schema comune in JavaScript è l'auto-esecuzione di funzioni anonime. Questo schema crea una espressione funzione e la esegue immediatamente. Questo schema è estremamente utile nel caso si voglia eliminare l'inquinamento dello spazio globale dei nomi con il codice. Infatti nessuna delle variabili dichiarate all'interno della funzione sono visibili al di fuori di essa.

Ecco un esempio di Self-Executing Anonymous Function


(function(){
    var foo = 'Hello world';
})();
console.log(foo);   // undefined!

C'è però ancora differenza con:


reader.onload=(function(theFile){
   return function(e){
      var span = document.createElement('span');
      span.innerHTML = ['<img class="thumb" src="', e.target.result,
         '" title="', escape(theFile.name), '"/>'].join('');
      document.getElementById('list1').insertBefore(span, null);
   };
})(f);
reader.readAsDataURL(f);

Cosa fa reader.readAsDataURL(f)?

Legge il file f. Se è così significa che ( function...)(f)