ES10 news: una panoramica delle feature di ES2019

E

ES10 (es2019) news

Il comitato Ecma TC39, che è responsabile delle specifiche del nostro benamato ECMAScript, JavaScript per gli amici, ha da poco confermato la lista di feature che erano in stage 4. Questo significa che esse diventeranno parte delle specifiche di ES2019.
Con questo tweet Mathias Bynens ci conferma che non ci sono altre stage 4 proposals in attesa, perciò a quanto pare le features che elencheremo saranno quelle definitive per questa versione del linguaggio.

Quali novità sono state implementate quest’anno?

  • Array.prototype.{flat,flatMap}
  • Object.fromEntries
  • String.prototype.{trimStart,trimEnd}
  • Symbol.prototype.description
  • Optional catch binding
  • Array.prototype.sort(): obbligatoria un’implementazione stabile
  • JSON.stringify: revisione
  • JSON superset
  • Function.prototype.toString: revisione

Vediamo di analizzarle una per una!

 

Array.prototype.flat

Motivo di gioia, assieme alla sua cugina flatMap, per tutti i functional programmers, ci permette di eseguire l’operazione flat su un array che contiene al suo interno uno o più array ad un qualsiasi livello di profondità.
Profondità che è impostata ad 1 di default, ma può essere modificata secondo la necessità in fase di chiamata del metodo:

const arr = [1, 2, [3, 4]];
arr.flat();  // [1, 2, 3, 4]

const arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat(); // [1, 2, 3, 4, [5, 6]]
arr2.flat(1); // [1, 2, 3, 4, [5, 6]]
arr2.flat(2); // [1, 2, 3, 4, 5, 6]

Nel caso in cui non conoscessimo a priori il possibile livello di innestamento degli array, possiamo utilizzare il valore numerico Infinity:

[[[[1,2],[[3,4]]]]].flat(Infinity);
// [1,2,3,4]

 

Array.prototype.flatMAP

Nel caso in cui necessitassimo di eseguire un’operazione di trasformazione sugli elementi di un array e il singolo risultato potrebbe essere un array a sua volta, alla fine ci ritroveremmo a dover gestire un array di array:

const admins = [
    {
        name: 'Dario',
        aka: 'frongy',
    },
    {
        name: 'Giacomo',
        aka: 'giac',
    },
    {
        name: 'Paolo',
        aka: 'rizzo',
    },
    {
        name: 'Andrea',
        aka: 'jfet',
    },
];

// operazione di mapping che produce un array di array
admins.map(admin => [admin.name, admin.aka]);
/*
[
    ['Dario', 'frongy'],
    ['Giacomo', 'giac'],
    ['Paolo', 'rizzo'],
    ['Andrea', 'jfet'],
]
*/

Se volessimo anzi un array monodimensionale come risultato dell’intera operazione, possiamo sfruttare il metodo flatMap.
Questo metodo combina una operazione map con una un operazione flat di profondità 1.
Il metodo flatMap richiede infatti come argomento una mapper function:

// map + flat(1)
admins.map(admin => [admin.name, admin.aka]).flat(1);
/*
[
    'Dario', 'frongy',
    'Giacomo', 'giac',
    'Paolo', 'rizzo',
    'Andrea', 'jfet',
]
*/

// flatMap
admins.flatMap(admin => [admin.name, admin.aka]);
/*
[
    'Dario', 'frongy',
    'Giacomo', 'giac',
    'Paolo', 'rizzo',
    'Andrea', 'jfet',
]
*/

Questo tipo di operazione è particolarmente utile quando dobbiamo unire, in qualche modo, due diversi container del medesimo tipo, ma non vogliamo ricadere nella situazione in cui abbiamo più livelli innestati del container.
Sicuramente conoscerete già un’operazione simile in JavaScript ovvero il metodo then delle Promise, il quale ci permette di eseguire un’operazione di trasformazione del valore contenuto nella Promise grazie ad una callback. Se infatti la callback dovesse restituire una Promise a sua volta, non otterremo una Promise di Promise (quindi uno o più livelli innestati del container): i due container, le due Promise, vengono fuse assieme.

 

OBJECT.FROmENTRIES

Questa utility ci permette di creare un oggetto a partire da una lista di coppie chiave-valore.

Rinfreschiamoci un attimo la memoria e vediamo un paio di modi per ottenere una di queste liste:

1) Object.entries()
const obj = {
    prop: 'value',
    prop2: 42,
}

Object.entries(obj);
/*
[
    ['prop', 'value'],
    ['prop2', 42],
]
*/
2) spread di una mappa
const map = new Map();

map.set('prop', 'value');
map.set('prop2', 42);

[...map];
/*
[ 
    ['prop', 'value'], 
    ['prop2', 42],
]
*/

L’utility Object.fromEntries può quindi essere considerata l’opposto di Object.entries:

const obj = { 
    prop: 'value', 
    prop2: 42, 
}

const listOfKeyValuePairs = Object.entries(obj);

Object.fromEntries(listOfKeyValuePairs);
/*
{ 
    prop: 'value', 
    prop2: 42, 
}
*/

 

Qualche particolare in più:

• chiavi duplicate
// se una chiava compare più volte nella lista
// viene sovrascritta

Object.fromEntries(
    [
        ['key', 42],
        ['key', 70];     
    ]
);
/*
{
    key:70
}
*/
• simboli usati come chiavi

Mentre il metodo Object.entries ignora le proprietà che possiedono come chiave un simbolo, l’utility Object.fromEntries ci permette di impostare uno o più simboli come chiave/i.

• COERCIZIONE DELLE chiavi

Gli unici tipi validi per le chiavi in un oggetto sono stringhe e simboli. Perciò se una coppia chiave-valore contenesse una chiave di altro tipo, essa verrà coercizzata in stringa.

• ITERABILi e oggetti array-like

Il metodo Object.fromEntries è molto flessibile. La lista di chiavi-valori non deve essere obbligatoriamente un array, ma può essere un qualsiasi iterabile. E la singola coppia chiave-valore non deve essere per forza un array, ma può essere un qualsiasi oggetto che possiede due proprietà aventi chiavi ‘0’ e ‘1’.

 

String.prototype.trimStart

String.prototype.trimEnd

Sicuramente avete utilizzato più di una volta il metodo trim su una stringa, che ci elimina eventuali whitespace presenti sia all’inizio che alla fine della stringa stessa:

"  ciao  ".trim(); // "ciao"

Adesso sarà possibile eseguire questa operazione solo su uno dei due estremi della stringa:

"  ciao  ".trimStart(); // "ciao  "

"  ciao  ".trimEnd(); // "  ciao"

 

Symbol.prototype.description

Quando creiamo un simbolo, possiamo passare alla factory function un parametro di tipo stringa che verrà utilizzato come descrizione del simbolo:

const sym = Symbol('Descrizione');

Questa descrizione viene utilizzata quando si converte esplicitamente un simbolo in una stringa:

const sym = Symbol('Descrizione');

sym.toString(); // "Symbol('Descrizione')"
String(sym); // "Symbol('Descrizione')"

Adesso è possibile accedervi direttamente tramite la read-only property description, la quale mostra direttamente la descrizione:

const sym = Symbol('Descrizione');

sym.description; // "Descrizione"

 

Optional catch binding

Questa feature ci permette di scrivere quanto segue:

try {
    // ...
} catch {
    // ···
}

quando non siamo interessati ad utilizzare l’errore nel blocco catch.

 

Array.prototype.sort

Il metodo sort utilizzabile su un qualsiasi array è obbligato adesso ad utilizzare un’implementazione stabile. Il motore V8 che esegue il JavaScript su Chrome e Node utilizzava precedentemente una versione del QuickSort non stabile.
Adesso viene utilizzato il TimSort, che è un algoritmo stabile.

 

well-formed JSON.stringify

Una delle specifiche del formato JSON impone l’uso della codifica UTF-8 quando un testo in formato JSON viene scambiato con entità al di fuori del nostro ecosistema. Ad esempio un testo JSON utilizzato nella comunicazione client-server, dove due ecosistemi devono comunicare.
Prima di ES10 poteva accadere che il metodo stringify dell’oggetto JSON ritornasse sequenze di caratteri codificate con UTF-16.

 

JSON superset

Prima di ES10 il formato JSON non era un subset perfetto dell’EcmaScript. Le stringhe in JavaScript non potevano contenere caratteri come U+2028 LINE SEPARATOR e U+2029 PARAGRAPH SEPARATOR, costringendoci ad utilizzare sequenze di escape.
Invece le stringhe JSON potevano e possono contenere senza problemi quei caratteri.

Adesso la restrizione che creava il problema nelle stringhe JavaScript è stata rimossa.

 

Function.prototype.toString

L’output del metodo toString invocato su una funzione adesso può essere riassunto nel seguente modo:

  • Ogni volta che è possibile restituirà il corpo della funzione. Perciò se una funzione è stata creata in JavaScript, il metodo dovrà restituirne il codice sorgente.
  • Le funzioni che sono implementate direttamente dagli engine che eseguono il JavaScript produrranno un placeholder standardizzato. In particolare il corpo della funzione sarà { [native code] }. Corpo che non potrà essere usato con certezza dalla funzione eval, né ora né con future versioni del linguaggio.

 

CONCLUSIONE

Rispetto ai precedenti anni non ci sono stati grandi stravolgimenti nella sintassi del linguaggio. Le uniche features degne di nota sono probabilmente i metodi flat e flatMap aggiunti agli array e l’utility Object.FromEntries.

Dalla versione 7.3 del motore V8 tutte queste features sono già utilizzabili in Chrome. Anche le ultime versioni di Firefox sembrano supportarle già tutte, perciò potete provare a giocarci un po’ da subito.

Approfondimenti

http://2ality.com/2018/02/ecmascript-2019.html

 

A proposito di me

Andrea Simone Costa

Classe 1997, Toscano DOCP, Asimov oriented.
Si è diplomato come perito elettronico, ma ha ben presto tradito le origini per immergersi nell'universo JavaScript. Ama condividere ciò che ha imparato in modo semplice e pragmatico, senza mai ricadere nel banale, coltivando segretamente il sogno di insegnare.

Gli articoli più letti

Articoli recenti

Commenti recenti