img

"Guida alle NOVITÀ JavaScript "ECMAScript-262 ver.5"

ECMAScript-262 ver.5

Sebbene sviluppare codice JavaScript sia molto più agevole che in passato, le specifiche di questo linguaggio risalgono ormai a più di 10 anni fa e necessitano quindi di un ammodernamento per venire incontro alle nuove esigenze dei siti web di ultima generazione.
Con l'introduzione di HTML5, ECMA ha colto la palla al balzo e ha deciso di introdurre la nuova versione specifiche del JavaScript:
ECMAScript-262 ver.5


PRIMA NOVITÀ

ECMAScript5: Modalità strict.

La modalità strict(non supportata in versioni IE precedenti alla ver.10) consente di introdurre nel codice un migliore controllo degli errori.

Dichiarazione della modalità strict
È possibile dichiarare la modalità strict aggiungendo "use strict";
all'inizio di un file, di un programma o di una funzione. Questo tipo di dichiarazione è noto come prologo delle direttive.
L'ambito di una dichiarazione della modalità strict dipende dal contesto in cui si dichiara.

La direttiva "use strict"

ECMAScript 5 ha introdotto la direttiva "use strict" al fine di utilizzare il linguaggio JavaScript in modo più robusto e sicuro, ed è impiegabile a livello di script generale oppure a livello di funzione specifica scrivendola come prima istruzione. Le differenze più importanti tra lo strict mode e il non-strict mode sono elencate di seguito.

  • Nello strict mode tutte le variabili devono essere dichiarate, ossia deve essere sempre esplicitata la parola chiave var, altrimenti verrà generato un errore di tipo ReferenceError. Nel non-strict mode è possibile assegnare un valore a una variabile dichiarata senza la parola chiave var e la stessa diverrà una proprietà dell'oggetto globale.

  • Nello strict mode le funzioni invocate come tali hanno la parola chiave this undefined. Nel non-strict mode la parola chiave this punta all'oggetto globale.

  • Nello strict mode è un errore di sintassi definire nell'ambito di un oggetto creato con la sintassi letterale due o più proprietà con lo stesso nome. Nel non-strict mode non c'è alcun problema.

  • Nello strict mode è un errore di sintassi definire una funzione con due o più parametri con lo stesso nome. Nel non-strict mode non c'è alcun problema.

  • Nello strict mode il tentativo di assegnare un valore a una proprietà non scrivibile oppure di creare una nuova proprietà per un oggetto non estensibile causerà il lancio di un'eccezione di tipo TypeError. Nel non-strict mode tali tentativi falliranno senza alcun avvertimento.

  • Nello strict mode il tentativo di cancellare con delete una proprietà non configurabile causerà il lancio di un'eccezione di tipo TypeError. Nel non-strict mode tale tentativo fallirà e delete ritornerà false.


esempio:
function trovaProdotti(numeri) {
    "use strict";
    var prodotti = 0,
        len = numeri.length;
    for(var i = 0; i < len; ++i) {
      var numProdotti = prodotti * numeri[i];
    }
    return prodotti;
}
print(trovaProdotti([1, 2, 3, 4, 5]));

IL DOCUMENT OBJECT MODEL (DOM) - CON ECMAScript.5

JavaScript è un linguaggio che permette di manipolare gli oggetti presenti nella pagina, al fine di favorire l'interazione dell'utente con la pagina stessa.
Per interagire con gli oggetti presenti sulla pagina, JavaScript deve avere a disposizione un contenitore dove andare a recuperare questi oggetti.
Questo contenitore è il Document Object Model (DOM).
Quando una pagina viene inviata al browser, il browser interpreta il codice HTML e lo renderizza.
Allo stesso tempo, il browser crea un oggetto in memoria per ogni frammento HTML (sia esso un tag input, un testo, un'immagine o qualunque altra cosa) Alla fine, tutti gli oggetti creati vengono inseriti in una struttura unica: il DOM.

Il DOM ha una struttura ad albero che è equivalente a quella della pagina. Così come possiamo annidare tag, testi e altro ancora, anche nel DOM gli oggetti sono annidati.
Il DOM introduce un livello di astrazione tra l'HTML e il nostro codice JavaScript.

UTILIZZARE I SELETTORI PER NAVIGARE IL DOM

Ai tre selettori, già presenti nella precedente versione di JavaScript:

var oggetto = document.getElementsByTagName("p");
var oggetto = document.getElementById("mioID");
var oggetto = document.getElementsEyClassName
("miaClasse");

A partire dalla versione 5 di JavaScript, sono stati introdotti due nuovi metodi che offrono molta più flessibilità nella ricerca degli oggetti nel DOM.

I nuovi metodi introdotti sono querySelector e querySelectorAll.

Il metodo querySelector effettua una query sul DOM e restituisce il primo oggetto restituito dalla query. Se la query restituisce più oggetti, al codice viene restituito solo il primo. Se la query non restituisce nemmeno un oggetto, viene restituito null.

Il metodo querySelectorAll effettua una query sul DOM e restituisce tutti gli oggetti recuperati. Qualunque quantità di oggetti sia recuperata dalla query (nessuno, uno o più di uno) il metodo querySelectorAll restituisce una lista.

Esempio:

var oggetto, lista;

oggetto = document.querySelector("#mainText");
Recupera il primo oggetto con ID mainText

oggetto = document.querySelector("div");
Recupera il primo tag div

lista = document.querySelectorAll("div");
Recupera tutti div

lista = document.querySelectorAll(".miaClasse");
Recupera tutti i tag con classe CSS "miaClasse"

lista = document.querySelectorAll("div p");
Recupera tutti i tag p contenuti in un tag div

lista = document.querySelectorAll("div > p");
Recupera tutti i tag p figli diretti di un tag div

lista = document.querySelectorAll("form input[disabled]");
Recupera tutti gli elementi disabilitati

lista = document.querySelectorAll("form input[type=‘checkbox']");
Recupera tutti i checkbox



UTILIZZO DEGLI ARRAY - CON ECMAScript.5

Con ECMAScript5, sono stati introdotti nuovi metodi che semplificano l'utilizzo della classe Array:
e sono:

    forEach
    every e some
    filter e map
    indexOf e lastIndexOf
    reduce e reduceRight

Per l'analisi di questi metodi useremo gli oggetti JavaScript e HTML mostrati di seguito.

    var mioArray = ["m1", "m2", "m3", "m4"];
    var arrayNumerico = ["10", "7", "3", "5",];
    var indexArray = ["ia1", "ia2", "ia3", "ia2"];
    
    //HTML,
    <span id="uscitaSpan"></span>

Analisi del metodo forEach.

Il metodo forEach itera su tutti gli elementi dell'array e per ognuno di essi invoca un metodo che noi definiamo passando in ingresso l'elemento corrente dell'array.
Nel nostro metodo possiamo eseguire qualunque azione eccetto aggiungere o rimuovere elementi all'array soggetto all'iterazione.

    function forEach() {
      uscitaSpan.innerHTML = "";
      mioArray.forEach(function (item) {
        uscitaSpan.innerHTML += item + " ";
        });
    }

In questo esempio il metodo forEach itera sugli elementi dell'array e chiama la nostra funzione,
che non fa altro che scrivere il valore dell'elemento corrente dell'array nello <span> di uscita.

 <span>m1 m2 m3 m4</span>

Analisi dei metodi every e some.

I metodi every e some sono molto simili tra loro. Il metodo every valuta se tutti gli oggetti dell'array rispettano una determinata condizione (specificata dal programmatore).
Se tutti gli oggetti rispettano la condizione, il metodo restituisce true, altrimenti restituisce false.
Il metodo some fa la stessa cosa del metodo every con la sola differenza che se anche un solo oggetto nell'array rispetta la condizione, allora il metodo restituisce true, altrimenti it metodo restituisce false.

function everyTest() {
 var numeriEvery = arrayNumerico.every(function (value) {
 return (value % 2) == 0;
});

uscitaSpan.innerHTML = (numeriEvery) ? 
 "Tutti i numeri sono pari" :
 "Non tutti i numeri sono pari";

 function someTest() {
  var numeriSome = arrayNumerico.some(function (value) {
  return (value % 2) == 0;
 });
 
 uscitaSpan.innerHTML = (numeriSome) ? 
 "Alcuni numeri sono pari" : 
 "Tutti i numeri sono dispari";

In questo esempio abbiamo un array con quattro numeri. La funzione everyTest verifica che tutti siano numeri pari, mentre la funzione someTest verifica che ce ne sia almeno uno pari.
Come possiamo vedere dal codice (nello <span> di uscita), entrambi i metodi accettano in ingresso una nostra funzione che restituisce:
true se il numero a pari e false se e dispari.

  <span>Non tutti i numeri sono pari</span>
  //contiene il risultato del metodo everyTes
   
  <span>Alcuni numeri sono pari</span>
  //contiene il risultato del metodo someTest


Analisi del metodo filter

Il metodo filter filtra gli oggetti di un array, riversando quelli che rispettano una determinata condizione in un nuovo array.
Per fare questo, il metodo filter esegue un ciclo sugli elementi di un array e per ognuno di essi invoca un nostro metodo, passando l'elemento corrente.
Il nostro metodo verifica se l'elemento deve far parte del nuovo array di uscita e, se questo è vero restituisce true altrimenti restituisce false.
Il risultato del metodo filter è sempre un array, a prescindere da quanti elementi ci siano dentro.
Sia che ci siano zero, uno o più elementi, il risultato è sempre un nuovo array.
L'array originario su cui è stata effettuata l'iterazione rimane invariato.

    function filterTest() {
       uscitaSpan.innerHTML = "";
       var nuovoArray = arrayNumerico.filter(function (value) {
         return (value % 2) != O;
       });
       
      nuovoArray.forEach(function (item) {
       uscitaSpan.innerHTML += item + " ";
       });
   }

In questo esempio, utilizziamo il metodo filter per creare un nuovo array che contiene solo i numeri dispari dell'array originale. Dopo la chiamata al metodo forEach, lo <span> di uscita appare come segue:

    <span>7 3 5</span>
    //contiene il risultato della chiamata al metodo forEach
  

Analisi del metodo map

Il metodo map itera gli oggetti di un array, invoca un nostro metodo passando in ingresso l'oggetto corrente e infine riversa tutti gli oggetti in un nuovo array.
A differenza del metodo filter, map non esegue nessun filtro ma è utile se si devono modificare gli oggetti prima di riversarli nel nuovo array.
Così come avviene per il metodo filter il risultato del metodo è un nuovo array, mentre l'array originale rimane immutato.

    function mapTest() {
      var nuovoArray = mioArray.map(function (value) {
      return value.toUpperCase();
      });
    
    nuovoArray.forEach(function (item) {
      uscitaSpan.innerHTML += item + " ";
      });
    }

In questo esempio, utilizziamo il metodo map per creare un nuovo array che contiene gli elementi dell'array originale convertiti in maiuscolo.
Una volta eseguita anche la chiamata al metodo forEach, lo <span> di uscita appare come segue:

        <span>M1 M2 M3 M4</span>
        //contiene il risultato della chiamata al metodo forEach
      

Analisi dei metodi indexOf e lastIndexOf

I metodi indexOf e lastlndexOf ricercano un oggetto all'interno di un array e restituiscono rispettivamente l'indice (base zero) del primo elemento trovato e l'indice dell'ultimo elemento trovato
(nel caso l'array contenga più volte lo stesso elemento).

function indexOfTest() {
 uscitaSpan.innerHTML = 
 "Il primo elemento ia2 si trova nella posizione " +
 indexArray.index0f("ia2") + " (zero based) dell'array";
}

function lastIndexOfTest() {
 uscitaSpan.innerHTML = 
 "L'ultimo elemento ia2 si trova nella posizione " + 
 indexArray.lastIndex0f("ia2") + " (zero based) dell'array";
}

In questo metodo recuperiamo la posizione dell'elemento ia2 all'interno dell'array. Il risultato delle chiamate ai metodi è mostrato nello <span> di uscita, dove la prima riga mostra il risultato del metodo indexOfTest e la seconda del metodo lastIndex0fIest.

<span>Il primo elemento ia2 si trova nella posizione 1 
(zero based) dell'array</span>
 
<span>L'ultimo elemento ia2 si trova nella posizione 3 
(zero based) dell'array</span>

Analisi dei metodi reduce e reduceRight

Il metodo reduce permette di concatenare i dati di un array, ottenendo da questi un unico risultato. Ad esempio, se abbiamo un array di numeri, possiamo utilizzare reduce per ottenere la somma di questi numeri. Se abbiamo un array di stringhe, potremmo concatenare le stringhe per ottenerne una sola.

Il metodo reduceRight ha lo stesso scopo di reduce con la differenza che invece di partire dall'inizio dell'array, parte dalla fine, ritornando poi a ritroso.

Il metodo reduce itera sugli elementi dell'array e per ogni elemento chiama un metodo da noi definito. Questo metodo accetta in ingresso l'elemento corrente e il risultato della precedente invocazione al metodo.

function reduceTest() {
 uscitaSpan.innerHTML = "";
 var risultato = arrayNumerico.reduce(function (previousValue, value) {
 uscitaSpan.innerHTML += "'" + previousValue + "', + value + "' ";
 return parseInt(previousValue) + parseInt(value);
 });
 uscitaSpan.innerHTML += risultato;
}

Quando il metodo reduce comincia il ciclo, passa alla nostra funzione il valore del primo e del secondo elemento. Il metodo somma gli elementi 10 e 7 e restituisce 17. Nella successiva iterazione, il nostro metodo riceve come primo parametro la somma ottenuta dalla precedente invocazione (17) e come secondo parametro l'elemento corrente (3). Andando avanti in questo modo, otteniamo la somma totale degli elementi nell'array. Questo processo è chiaramente visibile nella figura 9.9, che mostra l'output del metodo reduceTest.

  <span>'10' '7'</span>
  <span>'17' '3'</span>  
  <span>'20' '5'</span>
  <span>'25'</span>

Miglioramenti alle proprietà con JavaScript 5

In ECMAScript 5 è stato introdotto un nuovo supporto per le proprietà che ne rende l'utilizzo molto più potente rispetto al passato. Adesso, possiamo configurare una proprietà per specificare se questa è modificabile, se ha un getter o un setter. Per fare questo, una proprietà non è più un valore semplice, ma una classe con attributi precisi.

Costruzione di una classe con una proprietà che ha un getter e un setter, utilizzando gli attributi.

var MiaClasse = Object.create(Object.prototype, { 
 propertyName: {
  enumerable: true,
  configurable: true,
  get: function() { return "propertyValue" },
  set: undefined
  }
});

Tramite il metodo Object.create definiamo la classe.
Il primo parametro rappresenta la classe da cui ereditare (object in questo caso)
mentre il secondo parametro contiene una lista di proprietà.
Per definire la proprietà non usiamo solo la coppia nome/valore, ma usiamo una coppia nome/attributi
per aggiungere informazioni alla proprietà.

Gli attributi sono:

  • enumerable — definisce se la proprietà è visibile quando si iterano tutte le proprietà dell'oggetto

  • configurable — definisce se gli attributi della proprietà possono essere modificati

  • value — definisce il valore della proprietà

  • writable — definisce se la proprietà è in sola lettura

  • get — definisce il getter della proprietà

  • set — definisce il setter della proprietà

Gli attributi sono tutti opzionali, in quanto la presenza di alcuni esclude la presenza di altri. enumerable e configurable possono essere sempre presenti e, per default, sono impostati a true.
value e writable sono mutualmente esclusivi con get e set.
I primi due si usano quando non abbiamo una proprietà che deve semplicemente contenere un dato e quindi non ha logica.
La seconda coppia la usiamo quando nel getter e nel setter dobbiamo inserire logiche come invocazione di metodi o controlli di validità del dato.

Anche se usiamo il metodo Object.create, la classe rimane modificabile, cioè possono essere aggiunte o eliminate proprietà a run time oppure possono essere modificati gli attributi.
Ma possiamo eliminare questa opzione utilizzando i metodi preventExtensions, seal e freeze della classe Object. (Anche questi metodi sono stati introdotti con ECMAScript 5)

così come mostrato nel seguente esempio:

Object.preventExtensions(MiaClasse);
Object.seal(MiaClasse);
Object.freeze(MiaClasse);

Il metodo preventExtensions rende l'oggetto non modificabile.
Il metodo seal rende l'oggetto non modificabile e imposta l'attributo configurable di tutte le proprietà dell'oggetto a false.
Il metodo freeze rende l'oggetto non modificabile e imposta gli attributi configurable e writable di tutte le proprietà dell'oggetto a false.