Skip to content
Aprende Angular de forma rápida y efectiva  Ver curso

Novedades de ES6 (parte 2)

Después de la introducción a ES6 -el Javascript moderno- y algunas de sus características, continuamos con el tema para ver más novedades de ES6, la nueva especificación de Javascript que se aprobó en 2015:

  • Operador de propagación (Spread operator)
  • Destructuring
  • Template Literals
  • Tagged template literals
  • For … of loop
  • Block level function declarations
  • Generadores

Operador de propagación (Spread operator)

El spread operator lo que nos permite es pasar un array de elementos a una función, convirtiendo cada uno de los elementos en un argumento. Se podría pensar en el spread operator como la versión inversa de los parámetros rest. Lo vemos mejor con un ejemplo.

Antes (en ES5), para pasar un array de elementos a una función como parámetros, usaríamos el método apply del siguiente modo:

//ES5
function f(x, y, z) { }
var args = [0, 1, 2];
f.apply(null, args);

Ahora en cambio lo podemos hacer poniendo 3 puntos delante del array, es decir, usando el spread operator:

//ES6
function f(x, y, z) { }
var args = [0, 1, 2];
f(...args);

Además, cualquier argumento puede aprovecharse de esta característica, con lo que podríamos sacar ventaja para cosas como las siguientes:

//ES6
//example1
function f(v, w, x, y, z) { }
var args = [0, 1];
f(-1, ...args, 2, ...[3]);

//example2
var parts1 = ['shoulder', 'knees'];
var parts2 = ['chest', 'waist'];
var lyrics = ['head', ...parts1, ...parts2, 'and', 'toes'];
//lyrics = ['head', 'shoulder', 'knees', 'chest', 'waist', 'and', 'toes']

En ES5 no es posible combinar los métodos apply y new, mientras que ES6 permite combinar new con el spread operator:

//ES6
var d = new Date(...dateFields);

Destructuring

El destructuring nos permite descomponer un array u objeto para asignarlo a un conjunto de variables.

//ES6
//destructuring array
var a, b, rest;
[a, b] = [1, 2];

//destructuring array
var foo = function() {
    return [175, 75];
};
var [height, weight] = foo();

En el caso de descomponer las propiedades de un objeto, es importante que las variables a las que van a parar tengan el mismo nombre que las propiedades que queremos asignar

//ES6
//destructuring object
({a, b} = {a:1, b:2})

//destructuring user
var user = {
    name: 'Peter',
    surname: 'Griffin'
};
var { name, surname } = user;

Además, podemos combinar el destructuring con el spread operator para hacer una asignación como la siguiente:

var a, b, iterableObj;
[a, b, ...iterableObj] = [1, 2, 3, 4, 5];

Template Literals

Los template literals (también denominados template strings en las primeras revisiones de ES6) son literales de texto que nos permiten embeber expresiones, utilizar varias lineas e interpolar expresiones.

Veamos un ejemplo de interpolación:

var a = 5;
var b = 10;
console.log(`Fifteen is ${a + b} and not ${2 * a + b}.`); // 'Fifteen is 15 and not 20.'

Y un ejemplo de string multilinea:

console.log(`string text line 1
string text line 2`);
// "string text line 1
// string text line 2"

Tagged template literals

Esto vendría a ser una forma más compleja de template literal, que nos permite modificar lo que devuelve un template literal a través de una función. La función de un tagged template literal recibe en primer lugar un argumento que contiene un array con los elementos del template literal que son literales, mientras que los siguientes argumentos se corresponden a los valores interpolados del template literal. Vamos a ver un ejemplo:

var name = 'Peter';
var last_name = 'Griffin';

function sayHello(strings, ...values) {
    console.log(strings[0]); // "Name "
    console.log(strings[1]); // ", surname "
    console.log(values[0]); // Peter
    console.log(values[1]); // Griffin

    return `Hello ${values[0]} ${values[1]}`;
}

var greeting = sayHello`Name ${name}, surname ${last_name}`;

console.log(greeting);// Hello Peter Griffin

En la web de Mozilla encontrarás más información sobre los template literals.

For … of loop

El for…of loop nos permite crear un bucle de iteración a través de colecciones (Array, string, Map, Set, …). Este tipo de bucle es equivalente al que podríamos hacer con un forEach, pero la sintaxis es más similar a bucles for en otros lenguajes, como por ejemplo Python.

//ES5
var numbers = [1,2,3,4,5];
numbers.forEach(function(value) {
    console.log(value);
});
//1, 2, 3, 4, 5
//ES6
var numbers = [1,2,3,4,5];
for(let item of numbers){
    //remember let is useful to define local vars
    console.log(item);
});
//1, 2, 3, 4, 5
//ES6
var word = "foo";
for(let item of word){
    console.log(item);
});
//"f", "o", "o"

La principal diferencia entre for…in y for…of es que el primero itera entre todas las propiedades enumerables de un objeto, mientras que el segundo no funciona con todos los objetos, sino que es especifico de colecciones, es decir, solo itera sobre los elementos de cualquier colección que contenga la propiedad Symbol.iterator

Comparemos la diferencia entre for..in y for..of:

//ES6
let iterable = [3, 5, 7];
iterable.foo = "hello";

for (let i in iterable) {
    console.log(i); // logs 0, 1, 2, "foo"
}

for (let i of iterable) {
    console.log(i); // logs 3, 5, 7
}

Block level function declarations

Con ES6 podemos declarar funciones a nivel de bloque de forma segura (ES5 lo desaconsejaba, por que sus scopes está diseñados a nivel de función).

Si miramos el siguiente ejemplo, veremos que en ES5 el log de f() siempre es 2:

//ES5
function f() { return 1; }

{
    console.log(f()); // 2
    function f() { return 2; }
    console.log(f()); // 2
}

console.log(f()); // 2

Lo que está pasando es que las llaves no crean un scope nuevo, y por tanto la función f() se redefine para el scope global.

En cambio, si hacemos lo mismo en ES6, vemos que f() devuelve el valor 2 dentro de las llaves, pero el valor 1 fuera.

//ES6

function f() { return 1; }

{
    console.log(f()); // 2
    function f() { return 2; }
    console.log(f()); // 2
}

console.log(f()); // 1

Esto es por que en ES6, como en muchos otros lenguajes de programación, el bloque que se define entre llaves genera un nuevo scope (block scope).

Generadores

Los generadores son funciones de las que se puede salir y volver a entrar y que conservan su contexto entre las reentradas. Así de primeras parece extraño, pero no lo es tanto, si vemos a los generadores como una herramienta para construir iteradores.

Un generador se declara con function* (la palabra clave function seguida de una asterisco). También se pueden definir funciones generadoras usando el constructor GeneratorFunction y una function* expression.

La llamada a una función generadora no se ejecuta inmediatamente, sino que devuelve un objeto iterador. Cuando llamamos al metodo next() del iterador, se ejecuta el cuerpo de la función hasta la primera expresión yield, que determina el valor a devolver (o se delega con yield* a otro generador).
El método next() devuelve un objeto con 2 propiedades:

  • value: El valor que devuelve la expresión yield
  • done: Indica si es el último yield del generador.

Vamos a clarificarlo con un ejemplo, donde aprovechamos para recordar también algo de destructuring:

function* idMaker(){
    var index = 0;
    while(index < 3)
        yield index++;
    yield "end";
}

var gen = idMaker();

while(true){
    let {value, done} = gen.next();
    if(done)
        break;
    console.log(value);
}
// 0, 1, 2, end

DETALLE: NO puedes utilizar new con los generadores, no son constructores. var obj = new idMarker; lanzaría un error.

Para completar el tema de los generadores, vamos a ver un ejemplo de un generador que referencia a otro con yield*. Además, aprovecho para recordar que al devolver un iterador, puede aprovecharse del bucle for…of:

var anotherGenerator = function*(i) {
    yield i + 1;
    yield i + 2;
    yield i + 3;
}

function* generator(i){
    yield i;
    yield* anotherGenerator(i);
    yield i + 10;
}

for(let val of generator(10)){
    console.log(val);
}
//10, 11, 12, 13, 20

Creo que son suficientes novedades por hoy, ¡¡es importante asimilarlas!! En próximos POST seguiré desgranando mejoras que trae consigo ECMAScript2015.

¡Saludos!

Published inES6Javascript

One Comment

Deja un comentario