Archivi tag: javascript

JavaScript closures

Nella letteratura su JavaScript mi sono imbattuto varie volte nelle “closures” soprattutto in riferimento al concetto di “Scope” di una variabile e di “Context“. Ma non avevo mai approfondito più di tanto l’argomento. Poi mi sono imbattuto in un  articolo –  javascript-closure – dove si dice :

 “ritengo che esistano due tipologie di programmatori JavaScript: chi sa come usare le closure e chi no”

La cosa mi ha incuriosito, forse le ho sempre usate senza rendermene conto forse mi sono perso qualche cosa delle potenzialità di JavaScript. Nel dubbio mi sono proposto di approfondire la questione, nell’unico modo che ritengo valido per ben comprendere i concetti in informatica: scrivere esempi esplicativi. Fortunatamente al termine del mio approfondimento mi sono reso conto che le ho sempre usate senza problemi anche se probabilmente non avevo ben chiaro il loro aspetto teorico.
Quest’uso di una risorsa senza aver chiara la sua natura è qualcosa che ha a che fare con la natura di JavaScript: un linguaggio molto flessibile e libero che si presta a diversi stili di utilizzo.
Provenendo da Java ho applicato pattern tipici di questo linguaggio e in questo modo ho utilizzato in modo, per me naturale, queste misteriose “closures”.
In questo articolo cercherò di spiegare come.
Nei vari articoli sulle “closures” che ho consultato si parte sempre dalla distinzione tra “Context” e “Scope”.
Semplificando al massimo con “Scope” ci si riferisce all’ambito di validità di una variabile che può essere “locale” – definita all’interno della funzione – o globale – definita al suo esterno – . JavaScript non è molto raffinato nella gestione dei diversi ambiti di validità delle variabili. Non esistono per esempio le variabili locali a livello di blocco per non parlare di quelle di classe ( le classi semplicemente non esistono in JavaScript) . Semplificando molto si può dire che le variabili locali di una funzione vivono all’interno di ogni singola istanza della funzione stessa.
Con il “Context”  le cose si complicano; per poterne dare un esempio bisogna considerare una funzione nidificata in un altra: tutte le variabile definite all’interno della funzione contenitore e esterne alla funzione nidificata appartengono al “Context”.
Nell’esempio es1. ‘n‘ è una variabile di “Context”.

es1.

var Add = function() {
    var n = 0;
    return function() {
        n+=1;
        return n;
    };
};

var add_es1=Add();
add_es1();
add_es1();
add_es1();
add_es1();
var x=add_es1();

var add_es2=Add();
x=add_es2();
x=add_es2();

Permette di implementare un contatore senza far riferimento ad una variabile globale in quanto la  vita di ‘n‘  sopravvive per così dire alle diverse chiamate della funzione, questo è l’esempio più banale e più diffuso sull’uso delle “closures“, nel caso specifico individuano l’ambito di validità della variabile ‘n‘.
L’esempio es2. è solo un caso un poco più complesso.

es2.

var Add = function() {
    var n = 0;
    return {
        sum: function(x) {
            n += x;
        },
        get: function() {
            return n;
        }
    };
};

var add1 = Add();
add1.sum(10);
add1.sum(15);
add1.sum(50);
var x = add1.get();

var add2 = Add();
add2.sum(100);
add2.sum(1);
var x = add2.get();

Notate che ad ogni nuova istanza ( var add= Add(n); ) si crea un nuovo “Context” completamente indipendente.
Chi viene da Java può considerare la variabile ‘n‘ come una variabile d’istanza.
Credo che la difficoltà a comprendere il “Context” in JavaScript dipenda dal fatto che è un linguaggio ad oggetti senza una definizione esplicita di classe. Ma forse questo è solo un codizionamento che deriva dalle mie esperienze.
L’esempio es3. ,che usa una notazione di moda in JavaScript, serve a confondere un poco le acque e quindi a provocare lo sforzo per meglio comprendere. Sembra funzionare perfettamente, ma se eseguite l’esercizio noterete che è possibile avere una sola istanza, in altri termini si comporta come un “singleton”.

es3.

var Add = (function() {
    var n = 0;
    return  function(x) {
        n += x;
        return n;
    };
})();

var add_es1=Add;
add_es1(10);
add_es1(20);
add_es1(30);
add_es1(100);
var x=add_es1(1);

var add_es2=Add;
add_es2(100);
x=add_es2(100);

Gli esempi es4. e es5. son quasi eguali, l’unica differenza è nella definizione della variabile ‘num‘. Nel primo è definita come variabile di “Context” , quindi utilizzando le “closures“, nel secondo è definita all’interno dell’oggetto ‘op‘. Si potrebbe definire in termini intuitivi come locale rispetto all’oggetto ‘op’ e “contestuale” rispetto ai metodi  – funzioni –  definite in ‘op‘. Notate che in questo caso si deve utilizzare la variabile ‘num‘ utilizzando la parola chiave ‘this‘.

es4:

var Operator = function() {
    var num = 0;
    var op = {
        set: function(n) {
            num = n;
            return this;
        },
        get: function() {
            return num;
        },
        add: function(n) {
            num = num + n;
            return this;
        },
        mult: function(n) {
            num = num * n;
            return this;
        },
        sub: function(n) {
            num = num - n;
            return this;
        },
        div: function(n) {
            num = num / n;
            return this;
        }
    };
    return op;
};

var op_es1 = Operator();
op_es1.set(1);
op_es1.add(10).mult(20);
var x = op_es1.get();

var op_es2 = Operator();
op_es2.set(1000);
op_es2.sub(300);
op_es2.div(10);
x = op_es2.get();

es5.

var Operator = function() {
    var op = {
        num:0,
        set: function(n) {
            this.num = n;
            return this;
        },
        get: function() {
            return this.num;
        },
        add: function(n) {
            this.num = this.num + n;
            return this;
        },
        mult: function(n) {
            this.num = this.num * n;
            return this;
        },
        sub: function(n) {
           this.num = this.num - n;
            return this;
        },
        div: function(n) {
            this.num = this.num / n;
            return this;
        }
    };
    return op;
};

var op_es1 = Operator();
op_es1.set(1);
op_es1.add(10).mult(20);
var x = op_es1.get();

var op_es2 = Operator();
op_es2.set(1000);
op_es2.sub(300);
op_es2.div(10);
x = op_es2.get();

La scelta fra le due soluzioni in questo caso è una questione di gusti, ma si possono presentare delle soluzioni nelle quali utilizzare una tecnica o l’altra permette di avere codice più leggibile. Concludendo si può dire che, al di là della diversa notazione, le “closures” permettono di definire delle variabile che possono considerarsi come le variabili d’ istanza di object in Java ed altri linguaggi ad oggetti (python, c++, ruby, ..).
Questa non è certo la spiegazione più rigorosa delle “closures” ma in un linguaggio “libero” come JavaScript forse la cosa più saggia è costruirsi degli schemi  di organizzazione del codice sulla base delle proprie esperienze con linguaggi più “disciplinati”.

Per provare gli esempi (uaclosure_es.tar.gz) è disponibile un semplice server.py; è necessario solo con IE (restituisce errore 200 quando la response alle request AJAX è un file locale) per tutti gli altri è sufficiente caricare il file index.html.

UaEjs: un template in Javascript

Mi ha sempre disturbato comporre la pagina seguendo il rendering dei dati sul server per poi inviarla al client. Mi rendo conto che è la tecnica più diffusa e che inoltre esistono centinaia di ottimi tool in tutti i linguaggi per eseguire la composizione della pagina.
Ma mi piace pensare che, quando il client effetta  una request per aggiornare i contenut,siano spediti  solo i dati e che ogni azione di impaginazione sia fatta dal client stesso, questa può apparire come una fissazione vista la tendenza prevalente. L’idea è nata dall’esigenza di realizzare un client completamente agnostico rispetto al server.Questo risultato si può ottenere solo limitando l’attività del server al solo invio di dati (in formato XML o JSON) e facendo carico al client di tutta l’attività di rendering. Avendo come riferimento all’architettura Model View Control si otterrebbe una situazione in cui le attività del modulo View e quelle del Control sarebbero effettuate nel client.
Questa scelta probabilmente farà inorridire tutti i cultori del PHP; questo non lo considero un grosso problema,purtroppo anche gli amanti di Django, Rails e tools simili saranno forse contrariati. Ebbene qust’ultimi sappiano che è possibile continuare ad usare i loro amati framework semplicemente rinunciando ad utilizzare la componente View.
L’accidente che mi ha convinto della mia scelta, oltre al fastidio di dipendere in maniera troppo vincolante dal tipo di server, è stato quello di realizzare un piccolo applicativo che funzionasse su di un DVD, un qualsiasi supporto USB, su qualsiasi computer e con qualsiasi sistema operativo.
Se il sito non prevede contenuti variabili si può realizzare con pagine HTML statiche;le cose vanno diversamente se i dati sono il risultato di un lavoro in itinere che deve andare avanti per anni e se gli operatori sono completamente digiuni di HTML e magari usano dei semplici fogli EXCEL per archiviare i dati. Peggio ancora se l’applicativo prevede delle query con JOIN fra tabelle, ad esempio nel caso di una biblioteca un report che colleghi autori e testi. E’ ovvio che in questo caso le pagine HTML statiche non vanno bene mentre si potrebbe immaginare di usare dei semplici file CSV peri dati, delegando al client anche eventuali attività per semplici query. A questo scopo esistono specifici tool per disporre di un sql semplificato dal lato client, in ogni caso non è complicato realizzare con Javascript semplici filtri e join. La parte più delicata resta quella del rendering dei dati .
Un’altra scelta è stata quella di utilizzare Javascript come linguaggio embedded e non un linguaggio di markup. In questo modo, oltre ad evitare la noia di imparare un nuovo linguaggio, si viene incontro alle abitudini dei numerosissimi utilizzatori di PHP, favorendo una loro eventuale conversione; soprattutto si possono utilizzare tutte le funzionalità di Javascript per i calcoli, la gestione delle stringhe e tutto il resto.
Vagando su internet alla ricerca di materiale per il mio progetto mi sono imbattuto in una pagina di John Resig (l’autore di jquery)
http://ejohn.org/blog/javascript-micro-templating/

Malgrado il suo commento minimalista:
“ I’ve had a little utility that I’ve been kicking around for some time now that I’ve found to be quite useful in my JavaScript application-building endeavors. It’s a super-simple templating function that is fast, caches quickly, and is easy to use. I have a couple tricks that I use to make it real fun to mess with.“
ho capito che era proprio quello che cercavo.
Accantonato rapidamente un acutissimo attacco d’invidia, mi sono messo a studiare il codice. Poi un poco perché volevo un strumento del quale padroneggiassi tutti i dettagli, un poco spinto da orgoglio senile ho deciso di usare la proposta di J.Resig solo come ispirazione per implementare un mio template.
Sono anni che lo uso, non mi ha mai tradito. La massima soddisfazione l’ho raggiunta quando sono riuscito a far funzionare lo stesso client – senza modificare una virgola – con Django, Rails, Tomcat, delle CGI e infine con dei files CSV.

Scaricando il codice si hanno ha disposizione diversi esempi ed un ambiente didattico per meglio comprenderne il funzionamento e l’architettura.

Il template si trova in uaejs.js
Espone due funzioni:
var html=UaEjs.build(ejs,data)

dove ejs è il modello e data sono in formato JSON
e
var rs=UaEjs.compile(ejs,data)
rs è un object  JSON  rs={html:<html source>,js:<javascript source>}
ed è comodo per il debug.

La notazione utilizzata è molto semplice:
Per immergere Javascript si utilizzano i tag <% %> per l’apertura e la chiusura.
Per assegnare una valore da visualizzare <%= %>.
Esempio:

HTML ospitante:

<div id='ua'></div>

Modello ejs:

 <h3><%=titolo%></h3>
 <ul>
 <% for(var i=0; i<materie.length; i++) {%>
 <li>
 <%=materie[i]%>
 </li>
 <%}%>
 </ul>
 <%=anno%>
 <br/>
 <%=loc%>

Dati:
{
“titolo”: “Discipline”,
“anno”: “2015”,
“loc”: “Italia”,
“materie”: [“lettere”, “lingue”, “filosofia”, “matematica”, “fisica”, “informatica”, “legge”, “chimica”]
}

Script nel file HTML ospitante:

<script type="text/javascript">
var ejs = UaRq.rqsGet("es.ejs", null, null, 'text').responseText;
var data = UaRq.getJson("es.json", {}, null, null);
var html = UaEjs.build(ejs, data);
document.getElementById('ua').innerHTML = html;
</script>

L’uso della libreria UaRq serve per gestire le chiamate AJAX. Può essere sostituita da un’altra equivalente. Bisogna fare attenzione alla lettura dei files JSON; se si leggono come semplici files di testo vanno convertirli in object JSON.
Nel codice allegato vi sono diversi esempi che permettono di visualizzare tutte le componenti in gioco:
dati nel formato JSON.
Template EJS
Javascript intermedio generato per eseguire il rendering
Sorgente HTML
HTML

E’ possibile fare delle prove con l’opzione edit disponibile per modificare sia JSON che EJS e poi provare con l’opzione compile.
L’ambiente di test è più complesso, ma sono disponibile degli script bash (es0.sh, es1.sh ..) che eseguono tutto il lavoro. Per effettuare i test è necessario node.js.
Per installarlo in Linux
apt-get install node.

 Gli esempi (uaejs.tar.gz) non sono molto commentati, ma sono contemplati diversi casi sia per quanto riguarda la soluzione adottata sia per la tipologia di dati.
Per provare gli esempi è disponibile un semplice server.py; è necessario solo con IE (restituisce errore 200 quando la response alle request AJAX è un file locale) per tutti gli altri è sufficiente caricare il file index.html.

come sostituire eval() in Javascript

Javascript è un linguaggio che suscita amori ed odi, nato per dare un poco di vita all’esangue staticità delle pagine HTML è diventato sempre più autonomo ed importante. L’avvento di AJAX ne ha ulteriormente aumentato l’importanza nella programmazione lato client e sono ormai miglia i tool e librerie disponibili.
Infine con nodejs è diventato possibile, ed interessante, anche il suo utilizzo lato server. In verità questo è possibile da tempo con rhino ma si tratta di una soluzione che dipende da java e quindi non offre gli stimoli sufficienti per sostituire java stesso.
Alcuni link per una panoramica sugli ultimi sviluppi di Javascript e sulle sue potenzialità:
crockford
In particolare  (anche in italiano) JavaScript:The Good Parts.
Javascript: Il linguaggio di programmazione più incompreso del mondo
Una reintroduzione a Javascript

In Javascript è disponibile per l’esecuzione dinamica del codice in formato string la funzione eval(codeString).
Ad esempio invece di scrivere x=2+3; si può scrivere x=eval(“2+3”);
L’esempio è banale e dubito che venga in mente a qualcuno di utilizzare eval() in un caso come questo. Ma vi sono molte situazione nelle quali la tentazione è forte.
Ma in quasi tutti i migliori manuali su Javascript si sconsiglia di utilizzare eval() in quanto la si considera una funzione pericolosa per la sicurezza. Ho trovato varie motivazioni per questa accusa, la più ricorrente è la possibilità di accedere a variabili globali nel codice che si passa alla funzione. Ma per questo aspetto la colpa iniziale è in un cattivo uso delle variabili globali più che nella funzione stessa. Inoltre  si accusa eval() di non essere particolarmente efficiente, ma si tratta di un’accusa che la potenza dell’attuale hardware tende a vanificare e, in ogni caso, bisognerebbe fare un paragone con la specifica soluzione adottata per sostituire eval().
Il dato di fondo è che non ho trovato una soluzione generalizzata per sostituirlo ma solo la raccomandazione di organizzare diversamente il codice.
Nell’implementare un template lato client mi sono trovato in conflitto tra il rispetto dei consigli di tanti autorevoli autori ed il fatto che la funzione eval() mi faceva maledettamente comodo.
Ho cercato quindi una soluzione generale per sostituire eval(). Naturalmente le problematiche riguardanti l’uso del codice in formato stringa sono ineliminabili e possono essere risolte solo attraverso controlli sul codice stesso.
In Javascript una funzione può essere dichiara in vari modi:
Funzione globale:
function fn1(arg1,arg2){return arg1*arg2+100;}
fn1(2,3); // 106

Funzione assegnata ad una variabile:
var fn2=function(arg1,arg2){return arg1*arg2+100;}
fn2(2,3); // 106

Funzione letterale:
var fn3=new Function(“arg1,arg2”,”return arg1*arg2+100;”);
fn3(2,3); // 106

la soluzione proposta sfrutta la definizione di Funzione letterale.

var fnEval = function() {
 try {
   var pars, code;
   var l = arguments.length;
   var args = [];
   if (l > 2) {
     pars = arguments[0];
     code = arguments[1];
     for (var i = 2; i < l; i++)
       args[i - 2] = arguments[i];
   } else if (l == 1) {
     pars = "pars";
     code = arguments[0];
   } else {
     alert("arguments error ([params],code,[arg0,arg1,..])");
   }
   var fn = new Function(pars, code);
   try {
     return fn.apply(this,args);
   } catch (re) {
     alert("runtime error " + re);
     return null;
   }
 } catch (se) {
     alert("syntax error " + se);
     return null;
   }
}

Esempi:

fnEval("return 2*3;");
//6

fnEval("pars","return 2 * 3 * pars;",100);
//600

fnEval("x,y,z","return 2 * 3 +x+y+z;",10,20,100);
//136

La soluzione proposta permette non solo di eseguire il codice dinamicamente, ma anche di passare dei parametri ed i loro valori, cosa che non è possibile con eval(). Rispetto all’uso esplicito di new Function(){pars,code} offre il vantaggio di una maggiore compattezza e soprattutto permette di distinguere fra eventuali errori di sintassi nel codice passato alla funzione ed errori di runtime. Per poter passare i parametri ed i rispettivi valori senza limitazione di numero è stato usato l’espediente di invocare la funzione nella modalità fn.apply(this,args); dover args è un array dei valori dei parametri estratto da arguments della funzione fnEval().

JSONP ci libera dal dominio

L’introduzione di AJAX ha permesso un vero salto di qualità nella realizzazione di siti dinamici dando la possibilità di creare dei client implementati con javascript che dialogano con il server senza essere costretti per ogni request a ricaricare l’intera pagina. Vi sono delle situazioni (campi di lookup, help, liste, ..) per le quali l’uso di AJAX è di fatto indispensabile.
Nei browser per motivi di sicurezza ( Same Policy Origin) è impossibile effettuare chiamate verso domini diversi da utilizzato per caricare la pagina.
In linea di massima questa limitazione non costituisce un problema nella progettazione di un sito, ma vi sono delle situazioni nelle le quali sarebbe estremamente comodo se non necessario violare tale vincolo.
Valga per tutte il caso di una lettura di un’immagine da Flickr. In ogni caso può sempre essere utile poter reperire dati da un dominio diverso specialmente quando si ha la possibilità di modificare il codice del client ma non quello del server.
Esiste però la possibilità di aggirare l’ostacolo sfruttando il tag <script> usato per caricare i file javascript. Infatti per tale tag non esiste nessun vincolo per il dominio di provenienza.
Precondizione per l’uso di questa tecnica è l’uso di JSON per incapsulare i dati, ma questo non è davvero un problema in quanto tale notazione è ormai uno standard nel dialogo client-server.
Il trucco è veramente geniale.
Il dominio sia:

http://www.altrodominio.org

il JSON da leggere:

{
"cognome": "Bruno",
"nome": "Giordano",
"datanasc": "01-01-1548",
"sesso": "m",
"luogonasc": "Nola"
}

Il server incapsula il JSON come argomento di una funzione “FnEsempio”

FnEsempio({
"cognome": "Bruno",
"nome": "Giordano",
"datanasc": "01-01-1548",
"sesso": "m",
"luogonasc": "Nola"
});

Funzione di CallBack che ha come argomento i dati in formato JSON.
Fate attenzione che la funzione, purtroppo, deve essere globale.

FnEsempio = function (data) {
alert(data);
/*
o quello che diavolo volete voi
*/
};

script creato dinamicamente .

var script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.setAttribute("src","http://www.altrodominio.org/jsonp_esempio");
document.head.appendChild(script);
document.head.removeChild(script);

Se il server usa una sua funzione di CallBack bisogna conoscerne il nome ed
utilizzare quello.
Se previsto, come nelle API di Flickr, è possibile passare come parametro al server il nome della vostra funzione di CallNack.
Se la funzione si chiamaFnMia l’URL diventa:

script.setAttribute("src","http://www.altrodominio.org/jsonp_esempio?jsoncallback=FnMia");

Il meccanismo è molto semplice: quando viene caricato lo script è eseguita la funzione di CallBack alla quale è passato come argomento il JSON che contiene i dati. Quindi  all’interno di tale funzione, da voi implementata,  avrete a vostra diposizione  i dati.
Non è necessario scrivere tutto il codice usato sopra nell’esempio. Una soluzione è disponibile in jQuery e la trovate nel file getjson.js :

$.ajax({
type: 'GET',
cache: false,
url: www.altrodomnio.org,
dataType: 'jsonp',
contentType: "application/json",
jsonpCallback: FnMia,
data: params,
success: fnOk,
error: fnErr
});

Una soluzione che funziona con la stessa logica ma non utilizza jQuery la trovate nel file uajsonp.js:

var UaJSONP = {
  load: function(url) {
  var script = document.createElement("script");
  script.setAttribute("type", "text/javascript");
  script.setAttribute("src", url);
  document.head.appendChild(script);
  document.head.removeChild(script);
  },
  call: function(url, callBack, fnOk) {
  eval(callBack + '=function(data){fnOk(data);' + callBack + '=null;}');
  UaJSONP.load(url);
  }
};

Infine vi propongo un’altra soluzione che ha il difetto di non essere standard rispetto alle API di siti tipo Flickr ma che è forse più semplice (questa è la mia impressione). Per quanto la cosa non sia rilevante credo anche che sia originale. Invece di incapsulare il JSON come argomento di una funzione, lo si trasforma in variabile con un  nome. Dato il solito JSON:

{
"cognome": "Bruno",
"nome": "Giordano",
"datanasc": "01-01-1548",
"sesso": "m",
"luogonasc": "Nola"
}

Diventa:

var nomeEsempio={
"cognome": "Bruno",
"nome": "Giordano",
"datanasc": "01-01-1548",
"sesso": "m",
"luogonasc": "Nola"
}

Vi faccio notare che è meno invasiva la modifica del JSON ma soprattutto che NON è necessario usare una variabile globale come nel caso di una funzione di CallBack. Il codice di questa soluzione lo rovate nel file uajsonp2.js.

var UaJSONP2 = {
    get: function(url, jsonName, fnCall) {
        this.fnCall = fnCall;
        this.jsonName = jsonName;
        var script = document.createElement("script");
        script.setAttribute("type", "text/javascript");
        script.setAttribute("onload", "UaJSONP2.callback()");
        script.setAttribute("src", url);
        document.head.appendChild(script);
        document.head.removeChild(script);
    },
    callback: function() {
        var f = new Function("pars", "return " + this.jsonName + ";");
        this.fnCall(f());
    }
};

Nell’esempio es11 il file es11.json:

var jsondata = {
    "esempio": "es11",
    "cognome": "Martini",
    "nome": "Roberto",
    "datanasc": "24-02-1980",
    "sesso": "m",
    "luogonasc": "Firenze"
}

Codice javascript:

var callback = function (data) {
                 console.log(data);
                };
UaJSONP2.get('http://127.0.0.1:8081/es11.json', 'jsondata', callback);

Gli esempi (uajsonp_es.tar.gz) non sono molto commentati e la grafica è spartana, ma sono contemplati diversi casi sia per quanto riguarda la soluzione adottata sia per la tipologia di dati; inoltre il codice è semplice. Gli esempi es01 e es10 utilizzano codice javascript senza le librerie, per meglio evidenziare la tecnica usata; es07 si connette a Flickr. Per provare gli esempi è disponibile un banale server in python chiamato con due porte diverse dagli script server8080.sh e server8081.sh (la roba in bat e cmd potete farvela da soli).