Novedades de ECMAscript

14 de diciembre 2018ComentariosjavascriptDavid Poza SuárezComentarios

ECMAscript es un estándar o especificación para lenguajes de scripting creado por ECMA International, y Javascript es una implementación del mismo, como también lo son JScript y ActionScript.

Los navegadores van adoptando las novedades que proponen las distintas versiones de ES con cierto desfase, por lo que para usar las últimas versiones del estándar, lo que se hace actualmente con librerías como React es programar usando el último ES, (actualmente ES9 o también llamado ES2018) y transpilar usando Babel a una versión ampliamente soportada por todos los navegadores (ES5).

Gracias a las últimas versiones de ecmascript, EC6 en adelante, tenemos algunas mejoras muy interesantes, como son:

let  y const

Let nos permite definir una variable al igual que lo hacíamos con var, pero en este caso el ámbito de la misma está limitado al bloque donde se usa en lugar de a la función más cercana, por lo que limitamos aún más su ámbito. Esto es muy útil para evitar cometer errores cuando modificamos una variable sin querer desde otro bloque de código.

Const tiene la misma funcionalidad de let pero además evita modificar el valor de la variable.

No olvidemos que en una constante de tipo array o en un objeto, sigue siendo válido cambiar sus elementos o propiedades respectivamente, porque eso es mutar la variable, no cambiarla por otra. Para evitar cualquier mutación usamos Object.freeze(obj);

Arrow functions

Una función flecha es una forma de definir las funciones anónimas escribiendo menos código, ya que nos ahorramos la palabra "function" e incluso las llaves y el return si el cuerpo de la misma tiene una sola sentencia.

const myFunc = function() {
  const myVar = "value";
  return myVar;
}

const myFunc = (str) => {
  const myVar = str + "value";
  return myVar;
}

//solo si tiene un parámetro podemos quitar los paréntesis.
const myFunc = str => {
  const myVar = str + "value";
  return myVar;
}

const myFunc = () => "value"

Uso de arrow functions en Filter, Map, Reduce

Las arrow function son especialmente útiles cuando usamos filter, map o reduce (disponibles desde ES5), ya que reciben funciones como parámetros.

El método filter() crea un nuevo array con todos los elementos que cumplan la condición implementada por la función dada.

const words = \['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'\];

const result = words.filter(word => word.length > 6);

console.log(result);
// salida: Array \["exuberant", "destruction", "present"\]

El método `**map()**` crea un nuevo array con los resultados de la llamada a la función indicada aplicados a cada uno de sus elementos.

const numeros = \[1, 5, 10, 15\];
const dobles= numeros.map(function(x) {
   return x \* 2;
});
// el array dobles tiene ahora: \[2, 10, 20, 30\]

El método reduce() aplica una función a cada valor de un array (de izquierda a derecha) para reducirlo a un único valor.

reduce recibe como primer parámetro la función que va a aplicarse a todos los elementos del array, y como segundo parámetro (opcional) un valor inicial que podrá ser utilizado dentro dicha función.

La función que pasemos como primer parámetro de reduce puede a su vez tener cuatro parámetros opcionales, según su orden serán:

  • valorAnterior: El valor devuelto (retornado) en la llamada anterior de la función, o el valorInicial.
  • valorActual: Elemento actual que está siendo procesado en el array.
  • indiceActual: Índice del elemento actual que está siendo procesado en el array.
  • array: El array sobre el cual se llamó el método reduce.
const array1 = \[1, 2, 3, 4\];
const reducer = (valorAnterior, valorActual) => valorAnterior + valorActual;
console.log(array1.reduce(reducer));
// salida: 10

Parámetros por defecto

Podemos establecer valores por defecto para cuando no especifiquemos un parámetro.

function funcion(valor = "Defecto") {
  return "Valor: " + valor;
}

Rest operator

Permite tener funciones con un número variable de parámetros.

function numero\_parametros(...args) {
  return "Has pasado " + args.length + " parámetros.";
}
console.log(numero\_parametros(0, 1, 2)); // Has pasado 3 parámetros.

Spread operator

Permite usar arrays en lugares donde se espera una lista de parámetros.

const arr = \[6, 89, 3, 45\];
const maximus = Math.max(...arr); // returns 89

Destructuring Assignment

Sirve para asignar las propiedades de un objeto a variables.

{ prop1:var1, prop2:var2 } = objeto

const objeto = {prop1: 3.6, prop2: 7.4, prop3: 6.54 };
const { prop1, prop2, prop3 } = objeto ;
// tendremos las variables prop1, prop2 y prop3 con sus valores correspondientes.

Podemos poner otros nombres de variable diferentes al de las propiedades: const { prop1:var1, prop2:var2} = objeto ;

Podemos usar el destructuring con objetos anidados:

const objeto = {prop1: {x:1, y:2}, prop2: {x:3, y:4} };
const { prop1:{x:var1}, prop1:{y:var2}} = objeto ; //var1=1, var2=2

También podemos asignar los elementos de un array a variables

const \[a, b,,, c\] = \[1, 2, 3, 4, 5, 6\];
estamos asignando 1 a la variable a, 2 a b, y 5 a c

Podemos usar el rest operator para hacer el destructuring de un array, de modo que obtendremos el resto del array en otro array.

const source = \[1,2,3,4,5,6,7,8,9,10\];
const \[a,b, ...arr\] = source;
// a=1, b=2 y arr=\[3,4,5,6,7,8,9,10\]

Otro uso del destructuring es pasar un objeto como parámetro de función, eligiendo qué propiedades pasamos y así evitar pasar todo el objeto.

const objeto = {a:1, b:2, c:3};
const func = function({a,c}){
    return (a + c)
}
console.log(func(objeto)); //4

Template strings

Para crear strings insertando variables sin tener que abrir y cerrar comillas ni usar el operador "+". Usamos ${var} para insertar una variable y rodeamos la cadena con la comilla simple invertida. Además podemos crear cadenas multilínea sin usar "\n".

const person = {
  name: "Zodiac Hasbro",
  age: 56
};
const greeting = \`Hello, my name is ${person.name}!
I am ${person.age} years old.\`;

Objeto literal compacto

Un objeto literal es una lista de pares nombre:valor envuelta entre llaves.

Ahora si tenemos un caso en que el nombre de las propiedades del objeto literal es igual al de la variable que le estamos asignando, podemos escribirlo de forma más simple:

const funcion = (x, y) => ({ x:x, y:y }); //antes
const funcion = (x, y) => ({ x, y }); //ahora
console.log(funcion(1,2)) //devuelve {x:1,y:2}

Funciones declarativas compactas

Recordemos antes, que una función declarativa es:

function sumar(a,b){
   return a+b
}

Mientras que una función de expresión es:

const sumar = function(a,b){
   return a+b
}

Cuando estamos asignando una función declarativa a una propiedad de un objeto, podemos asignarla eliminando la palabra function.

// antes
const bicycle = {
  gear: 2,
  setGear: function(newGear) {
    this.gear = newGear;
  }
};

// ahora
const bicycle = {
  gear: 2,
  setGear(newGear) {
    this.gear = newGear;
  }
};

Clases

Ahora disponemos de la sintaxis de clases típica de otros lenguajes como Java o Python.

// antes EC5
const Rectangulo = function(base,altura){
    this.base = base;
    this.altura = altura;
}
Rectangulo.prototype.calcArea= function() {
    return this.base \* this.altura;
};
const r = new Rectangulo(5, 10);

// ahora EC6
class Rectangulo {
  constructor(base, altura) {
    this.base = base;
    this.altura = altura;
  }
  calcArea() {
    return this.base \* this.altura;
  }
}
const r = new Rectangulo(5, 10);

Getters y setters

Desde EC6 podemos definir getters y setters. Un getter es una función que sirve para obtener el valor de una propiedad de un objeto y un setter es aquella que permite establecer el valor de la misma.

Esto nos permite seguir usando la notación de acceso a propiedades estándar para lecturas y escrituras pero podemos personalizar cómo se recupera o se modifica la propiedad.

class Clase {
    constructor(prop){
      this.\_prop= prop;
    }
    get getProp(){
      return this.\_prop \* 2;
    }
    set setProp(prop){
      this.\_prop= prop / 2;
    }
  }

const obj = new Clase(1);
let temp = obj.prop; // usa el getter
obj.prop = 2; // usa el setter

Import

Nos permite elegir qué partes queremos importar en el fichero, ahorrando memoria si no necesitamos todo el modulo completo.

import { function } from "path al modulo"

//Si queremos importar todos los contenidos de un modulo
import \* as nombre from "path al modulo";
nombre.funcion

//Si estamos importando un export default no ponemos las llaves
import function from "path al modulo"

Export

Muy relacionado con la anterior directiva, tenemos export para que podamos declarar nuestras funciones o variables como usables desde otro fichero cuando las importamos.

const funcion = (param) => {
   return param\*2;
}
export { funcion }
export const foo = "var";

Si solo queremos exportar un elemento en un módulo y no se trata de un var, let o const, podemos usar export default.