Visualizzazione post con etichetta object. Mostra tutti i post
Visualizzazione post con etichetta object. Mostra tutti i post

lunedì, dicembre 01, 2014

Esercitazioni su node.js: esercizio 3

Testo dell'esercizio
Scrivere un programma che utilizzi una singola operazione asincrona per leggere il contenuto di un file e stampare a video il numero di linee del file letto.
Il percorso completo del file verrà passato come primo argomento sulla linea di comando.

Suggerimenti
E' la versione asincrona dell'esercizio precedente. Dare un'occhiata a fs.readFile(...).
Riflettere su differenza programmazione sincrona vs asincrona.

Buon lavoro.

Una possibile soluzione

 var fs = require('fs'),  
     file = process.argv[2],  
     numeroLinee;  
   
     fs.readFile(file, function (err, contenutoFile) {  
        if (!err) {  
            numeroLinee = contenutoFile.toString().split('\n').length;  
            console.log(numeroLinee);  
        } else {  
            console.log("Problemi in lettura " + file);  
        }  
 });  

La lettura del file è asincrona, la funzione readFile non è bloccante.
Anche in questo caso si puo' specificare la codifica del file da leggere ed omettere l'invocazione del toString()

 var fs = require('fs'),  
    file = process.argv[2],  
    numeroLinee;  
   
    fs.readFile(file,'utf-8',function (err, contenutoFile) {  
        if (!err) {  
            numeroLinee = contenutoFile.split('\n').length;  
            console.log(numeroLinee);  
         } else {  
            console.log("Problemi in lettura " + file);  
         }  
 });  

Ho aggiornato su github il progetto, effettuate un git pull per aggiornare i sorgenti che avete in locale, come mostrato in questo post

Alla prox.
Ivan

Esercitazioni su node.js: esercizio 2

Testo dell'esercizio
Scrivere un programma che utilizzi una singola operazione sincrona per leggere il contenuto di un file e stampare a video il numero di linee del file letto.
Il percorso completo del file verrà passato come primo argomento sulla linea di comando.

Suggerimenti
Per lavorare con il filesystem avremo bisogno del modulo fs di node.js, che includiamo nel programma con l'istruzione

 var mioFs = require('fs');  

A questo punto date un'occhiata alla guida ufficiale del modulo fs, in particolare alla funzione mioFs.readSync(...) 

La funzione è sincrona e ritorna un Buffer, che è il modo standard che ha node.js di rappresentare un insieme di dati, in qualsiasi formato: ascii, utf-8, binario.

Se i dati sono in un formato testuale, basterà invocare il metodo toString(...) per ottenere la stringa corrispondente.

Metodo veloce per contare le linee in una stringa

 var stringa = "Quante linee\nci sono in\nquesta stringa?";  
 var linee = stringa.split('\n');  
 console.log(linee.length);  

Una possibile soluzione
 var fs = require('fs'),  
    contenutoFile = fs.readFileSync(process.argv[2]),  
    numeroLinee = contenutoFile.toString().split('\n').length;  
   
 console.log(numeroLinee);  

In questo caso, readFileSync è una funzione sincrona, ritorna a sinistra un Buffer di dati, che deve essere convertito in stringa con il metodo toString().

Alternativamente si puo' specificare la codifica particolare con cui leggere il file ed omettere la trasformazione con il toString()
 var fs = require('fs'),  
    contenutoFile = fs.readFileSync(process.argv[2], 'utf-8'),  
    numeroLinee = contenutoFile.split('\n').length;  
   
 console.log(numeroLinee);  

Ho aggiornato su github il progetto, effettuate un git pull per aggiornare i sorgenti che avete in locale, come mostrato in questo post

Alla prox.
Ivan

Esercitazioni su node.js: esercizio 1

Con questo post, comincia una serie di esercitazioni pratiche, tratte dal sito nodeschool.io, per prendere confidenza con le librerie del linguaggio.

Testo dell'esercizio
Scrivere un programma che accetti uno o più numeri come parametri da linea di comando e che stampi a video la somma di tali numeri.
Esempio di invocazione del programma: node programma.js 2 3 5 , in tal caso l'output sarà 10.

Suggerimenti
Per risolvere l'esercizio, dare un'occhiata alla documentazione ufficila sull' oggetto globale process, in particolare alla proprietà process.argv.

Esempio di utilizzo di un ciclo for in javascript

 var indice,  
   max = 10;  
   for (indice = 1; indice <= max; indice++) {  
        console.log(indice);  
   }  
          

Una possibile Soluzione
 var somma = 0,  
     i = 0;  
   
 if (process.argv.length > 2) {  
      for (i = 2; i < process.argv.length; i++) {  
           if (Number(process.argv[i])) {  
              somma += parseInt(process.argv[i]);  
           }  
           else {  
              console.log("Parametro " + process.argv[i] + " non è un numero!" );  
           }  
      }   
      console.log(somma);  
 } else {  
      console.log('Esempio di utilizzo : node programma.js 1 3 5');  
 }  

Per convertire una stringa in un intero, ho utilizzato la funzione Number che trasforma una stringa nell'intero equivalente o ritorna NaN (Not a Number) nel caso in cui la stringa non rappresenti un numero.

In javascript esistono i concetti di valore falsy e truthy per le espressioni booleane: i numeri sono considerati valori veri mentre NaN, null, undefined valori falsi: Number('ertttt') ritorna dunque NaN che nell'if viene valutato a false.

Ho aggiornato su github il progetto, effettuate un git pull per aggiornare i sorgenti che avete in locale, come mostrato in questo post

Alla prox
Ivan

martedì, novembre 25, 2014

Programmazione asincrona e callbacks

Una analogia per capire la differenza tra programmazione sincrona e asincrona.
Normalmente gli uomini si comportano in maniera sincrona, le donne in modo asincrono :))
Quando faccio il caffè al mattino, aspetto quei 4 secondi davanti alla tazzina che si riempie.
Mia moglie, durante quei 4 secondi magari comincia a svuotare la lavastoviglie.

In java, normalmente, l'esecuzione delle istruzioni avviene in maniera sincrona, una istruzione viene eseguita non prima che la precedente abbia finito; se si vuole una sorta di parallelismo, bisogna usare i threads, con tutto il sovracarico (overhead), in termini di risorse, che ne consegue.

In javascript non esistono i threads. Tutte le nostre istruzioni vengono eseguite, sempre, in un unico thread ma esiste la possibilità di eseguire le istruzione in modalità asincrona.

Consideriamo lo scenario in cui dobbiamo effettuare una richiesta ad un server.

In modalità sincrona avremmo ad esempio

 richiesta = preparaRichiesta();  
 risposta = mandaRichiestaSincrona(richiesta);  
 visualizza(risposta);  

Se il server impiega 10 secondi a rispondere, il nostro codice è bloccato alla seconda istruzione, non si possono eseguire altre istruzioni nel frattempo.

In modalità asincrona avremmo invece, grazie alla possibilità di passare una funzione come parametro

 richiesta = preparaRichiesta();  
 mandaRichiestaAsincrona(richiesta, function(risposta) {  
   visualizza(risposta);  
 });  
 visualizza("Siamo in attesa di risposta dal server ...");  

In questo caso, la seconda istruzione è asincrona, dopo la sua esecuzione, il nostro programma continua con l'istruzione successiva: quando il server risponderà, con i suoi tempi, verrà eseguita la funzione di callback passata come argomento alla funzione asincrona.

Tecnicamente, il supporto alla programmazione asincrona è implementato, nell'interprete javascript, tramite l'utilizzo di

  • una coda di messaggi
  • un ciclo principale di gestione eventi
Tutte le istruzioni che devono essere eseguite dal nostro programma vengono inserite in una coda di messaggi che viene gestita da un ciclo principale che svuota ed esegue, in un unico thread, le istruzioni presenti nella coda.

Nel caso di una funzionalità asincrona, dipendente da un evento esterno al nostro programma, un componente del runtime di javascript aggiunge alla coda dei messaggi la funzione di callback da eseguire, con i relativi parametri, non appena l'evento si è verificato.

Le funzioni di callback non sono mai interrompibili, così come qualsiasi funzione in javascript: in questo modo non esite mai un problema di accesso ad aree di memorie condivise da thread diversi, visto che il ciclo principale viene eseguito in un unico thread.

Avrete già sentito l'acronimo AJAX: Asynchronous Javascript And Xml: è una tecnica che permette di effettuare richieste http ad un server in modalità asincrona, sfruttando il meccanismo visto sopra: quando il server risponde, viene eseguita la funzione di callback registrata con la richiesta, che, tipicamente, modifica il DOM (Document Object Model) della pagina html corrente.

Il DOM non è altro che la rappresentazione, ad albero, in memoria, del documento html.


Una sua modifica potrebbe essere, ad esempio, la creazione di una tabella con i dati restituiti dal server.

L'utente, mentre il server risponde, ha ancora la possibilità di interagire con la pagina corrente, generando eventi che possono essere gestiti dal ciclo principale in attesa della risposta del server.

E l'XML che c'entra? Storicamente era il formato scelto per rappresentare i dati scambiati tra client e server. Ora si preferisce il formato JSON e provate ad indovinare un pò il perche ...

Più in la, vedremo come implementare richieste asincrone a componenti lato server, componenti ovviamente implementati in javascript grazie alla piattaforma nodejs ;)

Alla prox.
Ivan

















lunedì, novembre 17, 2014

Ereditiamo o no?

In Javascript, non esistendo le classi, non esiste l'ereditarietà tra classi.

Esiste l'ereditarietà tra oggetti: un oggetto puo' ereditare, in fase di creazione, tutte le proprietà di un altro oggetto che costituisce il prototipo (prototype) dell'oggetto appena creato.

Ogni oggetto appena creato ha dunque un collegamento con il suo prototipo, oltre alle proprie proprietà (own properties).

Di default, ogni oggetto definito con json o con new Object(), ha come prototipo l'oggetto Object.prototype, che presenta una serie di proprietà (di tipo funzioni e quindi metodi) come hasOwnProperty(prop) che ritorna true se l'oggetto su cui si invoca contiene direttamente la proprietà indicata come argomento


 var automobile = {cilindrata: 1300, colore: 'rosso'};  
   
 console.log(automobile.hasOwnProperty('colore'));  

In questo caso verrà stampato sulla console il valore true.

Per la lista completa delle proprietà di Object.prototype rimando alla Guida Ufficile Javascript:
alcune di queste proprietà non sono standard, evitate di usarle per scrivere codice che funzioni su ogni browser.

Per creare un oggetto e assegnargli direttamente un prototipo è possibile usare la funzione Object.create(prototipo)


// o1 eredita da Object.prototype  
 var o1 = {b:3, c:4};  
   
 // o2 eredita da o1  
 var o2 = Object.create(o1)  
   
 o2.a = 1;  
   
 // o2.b nasconde o1.b  
 o2.b = 2;  
   
 // stampa true  
 console.log(o2.hasOwnProperty('a'));  
   
 //false  
 console.log(o2.hasOwnProperty('c'))  
   
 // stampa 4  
 console.log(o2.c);  

Anche le funzioni possono essere ovviamente definite nel prototipo e dunque ereditate

 var o = {  
  a: 2,  
  m: function(b){  
   return this.a + 1;  
  }  
 };  
   
 console.log(o.m()); // 3  
    
 // Quando chiamiamo o.m, in questo caso, 'this' si riferisce ad o  
   
 var p = Object.create(o);  
 // p è un oggetto che eredita da o  
   
 p.a = 12; // crea una own property 'a' su p  
 console.log(p.m()); // 13  
 // quando si invoca p.m, 'this' si riferisce a p.  

Riepilogando, ecco i diversi modi di costruire gli oggetti con le rispettive catene di prototipi

 var o = {a: 1};  
   
 // L'oggetto o ha Object.prototype come suo prototipo  
 // o non ha alcuna proprietà chiamata 'hasOwnProperty'  
 // hasOwnProperty è una proprietà posseduta of Object.prototype. 
 // Dunque o eredita hasOwnProperty da Object.prototype  
 // Object.prototype ha null come suo prototipo.  
 // o ---> Object.prototype ---> null  
   
 var a = ["ciccio", "puccio", "nicolino"];  
   
 // I vettori ereditano da Array.prototype (che ha metodi come indexOf, forEach, etc.)  
 // La catena dei prototipi di a è la seguente  
 // a ---> Array.prototype ---> Object.prototype ---> null  
   
 function f(){  
  return 2;  
 }  
   
 // Le funzioni ereditano fa Function.prototype (che ha metodi come call,   
 // bind, etc.)  
 // La catena dei prototipi di f è la seguente  
 // f ---> Function.prototype ---> Object.prototype ---> null  
   
 // Se usiamo una funzione costruttore  
 function Biblioteca() {  
  this.libri = [];  
 }  
   
 Biblioteca.prototype = {  
  aggiungiLibro: function(libro){  
   this.libri.push(libro);  
  }  
 };  
   
 var b = new Biblioteca();  
 // b è un oggetto che ha proprietà 'libri'   
 // Il prototipo di b è il valore di Biblioteca.prototype dopo   
 // l'invocazione di new Biblioteca().  

Quando dunque accediamo ad una proprietà di un oggetto, se la proprietà non è presente nell'oggetto stesso, viene risalita la catena dei prototipi fino ad arrivare a Object.prototype che ha prototipo null.

E' opportuno evitare di avere catene troppo lunghe per questioni di performance.

Alla prox.
Ivan

sabato, novembre 15, 2014

Spaghetti o Ravioli?

Siamo ora pronti a comprendere il codice presentato nel primo post, lo riscrivo qui per comodità

  var modulo = (function() {  
                   var f1 = function() {  
               
                            },  
                       f2 = function() {  
                 
                            },  
                       f3 = function() {  
                 
                            };  
               
                       return {  
                         f1 : f1,  
                         f2 : f2,
                         f3 : f3
                       };   
                })();  
   

Cosa contiene la variabile modulo? Contiene il risultato della esecuzione immediata della funzione dichiarata a destra, tale funzione ritorna il seguente oggetto


 {  
   f1 : function() {},  
   f2 : function() {}  
 }  
   
   

e quindi window.modulo espone le uniche proprietà f1 e f2 che in questo caso sono funzioni.

Le inner function, cioe le funzioni dichiarate all'interno di un'altra funzione, hanno l'importante proprietà di poter accedere alle variabili locali e i parametri dell'outer function anche quando l'outer function ha terminato la sua esecuzione.

Questa caratteristica, chiamata closure, insieme al concetto di  IIFE: Immediate Invocation Function Expression,  acronimo pomposo per descrivere una funzione che viene invocata appena subito dopo la sua dichiarazione, permette di creare degli oggetti che espongono solo le funzioni che vogliamo, nascondendo i dettagli implementativi; ciò ci permette di modularizzare il nostro codice, passando da un codice a spaghetti a un codice a ravioli :)

Facciamo ancora un esempio:

 var biblioteca = (function(nome) {  
        
      var libri = [],  
       
           nomeBiblioteca = nome,  
          
           inserisciLibro = function(libro) {  
               libri.push(libro);  
           },  
          
           getNomeBiblioteca = function() {  
               return nomeBiblioteca;  
           },  
          
           numeroLibri = function() {  
               return libri.length;  
           };  
   
          return {  
            insert : inserisciLibro,  
            getName : getNomeBiblioteca,  
            length : numeroLibri  
          };  
   
 })('civica di milano');  


 biblioteca.insert({titolo:'guerra e pace'});
 console.log(biblioteca.getName());
 console.log(biblioteca.length());

biblioteca è un oggetto, ritornato dalla funzione che viene immediatamente eseguita, espone dunque solo i tre metodi insert, getName, e length.

La variabile locale libri non è accessibile dall'esterno, essendo una variabile locale di una funzione,

I metodi dell'oggetto ritornato, possono ancora accedere alle variabili locali della funzione esterna, anche se la funzione esterna ha già finito la sua esecuzione: notate bene che in questo caso la eseguiamo immediatamente passandole il parametro 'civica di milano'.

Le linee di codice successive, memorizzano il libro dal titolo 'guerra e pace' nell'array libri, che esiste ancora nonostante la funzione precedente ha finito la sua esecuzione, stampano sulla console rispetivamente 'civica di milano' e 1.

Spero che ora non abbiate più dubbi sul potente concetto di closure, che ci permette di definire oggetti complessi nascondendo la loro complessita a chi deve utilizzarli.

Cominciate a utilizzare le IIFE e il vostro codice passerà da un groviglio di spaghetti a dei ravioli gustabili  uno ad uno :).

Alla prox.
Ivan

venerdì, novembre 14, 2014

Json e dintorni

Prima di affrontare il concetto chiave di closure, vediamo dunque di definire correttamente quello di oggetto in javascript.

Javascript non è tipizzato, le variabili dichiarate con la parola chiave var possono contenere numeri,stringhe,booleani (true o false), null e undefined.
I numeri, le stringhe e i booleani sono a tutti gli effetti degli oggetti, visto che hanno anche loro dei metodi, anche se sono oggetti immutabili.

Un oggetto è un contenitore di proprietà, dove  le proprietà, separate dalla virgola, hanno un nome ed un valore.
Una proprietà puo essere rappresentata da qualsiasi stringa, compresa la stringa vuota.
Il valore di una proprietà può essere un qualsiasi valore eccetto undefined

Gli oggetti sono utili per contenere e organizzare i dati, gli oggetti possono contenere altri oggetti e così possono rappresentare facilmente strutture ad albero o graphi.

Il modo standard di rappresentare gli oggetti in javascript è costituito dalle parentesi graffe aperte e chiuse che circondano zero o più coppie nome/valore.

 var oggetto_vuoto = {};  
   
 var dipendente = {  
   nome: "ivan",  
   cognome:"saracino"  
 };  
   
 var volo = {  
   compagnia: "alitalia",  
   numero: 345,  
   partenza: {  
    citta: "roma",  
    orario: "14/11/2014 13:30"  
   },  
   arrivo: {  
    citta: "milano",  
    orario: "14/11/2014 15:00"     
   }  
 };  

Ultimamente si usa il formato JSON (Javascript Standard Object Notation) per lo scambio dati tra compenenti distribuiti: il formato JSON nasce proprio dal linguaggio Javascript.

Per recuperare un valore da una proprietà di un oggetto

   
 // metodo standand di navigazione oggetti  
 volo.partenza.citta // roma  
   
 // metodo alternativo che mette in luce la natura di collezione associativa 
 // di coppie nome/valore  
 dipendente["nome"] // ivan  
   
 //proprieta noon presenti su oggetto  
 dipendente.nonesisto // undefuned  
   
 //tentativo di accedere a proprietà di proprietà inesistenti  
 dipendente.nonesisto.proprieta // eccezione a run time  

Per scrivere le proprietà di un oggetto

 // aggiornamento di una proprietà esistente  
 dipendente.nome = "ciccio";  
   
 // se una proprietà non esiste, viene creata  
 // dinamicamente!  
 dipendente.età = 43  

Le variabili che contengono gli oggetti sono dei puntatori agli oggetti in memoria.

Quando passiamo un oggetto come parametro, il passaggio del parametro è per copia  ( qui sono in disaccordo col buon Douglas Crockford che dice che il passaggio è by reference) come dimostrato dal seguente frammento di codice:

 var x = {p : 1};  
   
 var y = x;  
   
 y.p = 2;  
   
 x.p vale 2, stiamo modificando lo stesso oggetto   
 tramite lo stesso puntatore  
   
   
 definiamo una funzione che modifica la proprietà  
 di un parametro  
   
 var f = function(par) {par.p = 3};  
   
 invochiamo la funzione passando l'oggetto x  
   
 f(x);  
   
 x.p vale 3, nella funzione usiamo la copia   
 del puntatore x per modificare lo stesso oggetto in memoria  
   
 cerchiamo di cambiare l'oggetto puntato da par  
   
 var f = function(par) {par = {p:4}};  
   
 invochiamo nuovamente la funzione passando l'oggetto x  
   
 f(x);  
   
 x.p vale ancora 3, par è una copia del puntatore x,   
 copia che muore quando finisce la funzione f,  
 x punta all'oggetto originale  

In effetti il passaggio dei parametri è identico al linguaggio Java.

Ci sono altre considerazioni da fare sugli oggetti, ma credo sia opportuno dedicarci un post apposito.

Alla prox.
Ivan