¿Qué es ECMAScript 6?

ECMAScript 6, también conocido como ECMAScript 2015 o ES6, es la nueva versión de Javascript, aprobada en Junio 2015, y en la que se lleva trabajando desde 2011.

Se podría considerar que es una auténtica revolución en la sintaxis de Javascript. Su buque insignia es, probablemente, una clara orientación a clases y herencia, pero la verdad es que hay muchas otras novedades interesantes, como el uso de módulos, los parámetros por defecto, las variables let y const, o la novedosa sintaxis de las funciones arrow, entre otros cambios.

¿Se soporta ES6 actualmente?

Los principales navegadores ya implementan la mayoría de funcionalidades (ver compatibilidad actual con ES6), si bien aún están en proceso de adaptación, por lo que es recomendable utilizar un transpilador como Babel para convertir nuestro elegante ES6 en ES5 (el JS antiguo), y asegurar que nuestro codigo se podrá ejecutar en todos los navegadores.

Dicho esto, ya NO tienes excusa para retrasar tu aprendizaje de ES6: Vas a disponer de herramientas más modernas para desarrollar tu código, y gracias a los transpiladores funcionará en todos los navegadores.

Principales novedades de ES6

Vamos a ver algunas de las novedades más significativas de esta nueva versión de Javascript. En próximos posts, mostraré más novedades de ES6.

Variable const

De forma análoga a otros lenguajes, se ha definido un tipo de varaible que solo puede asignarse en su declaración, y no puede ser modificada.

const URL = 'www.mydomain.com';
URL = 'whatever'; // ERROR!!

Si una const se define dentro de un scope, solo pertenecerá a ese scope.

Variable let

La variable tipo let, a diferencia de var no puede ser accesible más allá de su scope.

(function() {
    console.log(global); // undefined
    console.log(local); // undefined
    if(true) {
        var global = "I'm global"; 
        let local = "I'm only local";
    }
    console.log(global); // I'm global
    console.log(local); //undefined
})();

Podemos observar como fuera del scope if(true){…}, la variable definida con let no existe, mientras que la definida con var, ha sido asignada al objeto raíz window por lo que podemos usarla como una variable global fuera de su scope.

Función arrow

Las funciones arrow proporcionan una sintaxis más compacta para la definición de funciones. Pongamos algunos ejemplos.

Si no hay argumentos, empezamos con 2 paréntesis y la flecha:

// ES5
setInterval(function(){
    console.log('hi world');
}, 100);
// ES6
setInterval(() => {
    console.log('hi world');
}, 100);

Con un argumento, no es necesario usar paréntesis:

// ES5
var vowels = ['a', 'e', 'i', 'o', 'u'];  
vowels.forEach(function(value){  
    console.log('vowel :' + value);
});
// ES6
var vowels = ['a', 'e', 'i', 'o', 'u'];  
vowels.forEach(value => {  
    console.log('vowel :' + value);
});

Con más de un argumento los paréntesis son obligatorios:

// ES5
var sum = function(a, b){ 
    return a+b;
}
// ES6
var sum = (a, b) => a + b; 

Además de simplificar la declaración de funciones, las arrow function tienen una peculiaridad, y es que no generan su propio valor this, con lo que si utilizamos la palabra reservada this en su interior, estaremos haciendo referencia al bloque en el que está definida la propia arrow function, sin necesidad de utilizar .bind o referenciar this desde otra variable.

Con un ejemplo quedará más claro:

En ES5 necesito usar .bind(this) para que el this de dentro del callback haga referencia al this de fuera.

//ES5
function User(name) {
  this.name = name;
  this.greeting = function(){
    setTimeout(function (response) {
      console.log("Hello " + this.name);
    }.bind(this), 500); 
  }
};

var user = new User("Enrique");
user.greeting();//Hello Enrique

En ES6, con una arrow function, puedo utilizar directamente el this del scope que contiene la arrow.

//ES6
function User(name) {
  this.name = name;
  this.greeting = function(){
    setTimeout((response) => console.log("Hello " + this.name), 500); 
  }
};

var user = new User("Enrique");
user.greeting();//Hello Enrique

Parámetros por defecto

Por fin podemos incluir valores por defecto en nuestros parámetros, como en otros lenguajes de programación. Podemos incluso referenciar otros parámetros:

// ES6
function greet(name, gender = 'Mr.', greeting = 'Hello ' + gender){
    console.log(greeting + ' ' +  name);
};

greet('Peter'); // Hello Mr. Peter
greet('Alex', undefined, 'Whats up'); //Whats up Alex

Parámetros Rest

Hasta ahora, cuando pasábamos argumentos a una función, se añadía una variable arguments que incluía todos los parámetros (definidos o no) que había recibido nuestra función.

// ES5
function printName(name){
  var length = arguments.length;
  var fullName = name;
    if(length > 1){
        for(var i=1; i< length; i++){
            fullName += ' ' + arguments[i];
        }
    }
  console.log(fullName);
};

printName('Felipe'); // Felipe
printName('Felipe', 'Juan', 'Froilan'); //Felipe Juan Froilan

Los parámetros Rest nos proporcionan una manera de pasar un conjunto indeterminado de parámetros que la función agrupa en forma de Array. Como detalle (de lógica), solo puede ser parámetro rest el último argumento de la función. Veamos mejor a qué me refiero.

// ES6
function printName(name, ...fancyNames){
  var fullName = name;
  fancyNames.forEach(fancyN => fullName += ' ' + fancyN);

  console.log(fullName);
};

printName('Felipe'); // Felipe
printName('Felipe', 'Juan', 'Froilan'); //Felipe Juan Froilan

Clases y herencia

¿Cuantas veces has deseado utilizar clases como dios manda en JS, en lugar de ensuciar el código con prototype y demás historias? Pués estás de suerte.

Donde en ES5 creamos clases y herencia del siguiente modo:

// ES5
//Class creation
function Document(title, author, isPublished) {
  this.title = title;
  this.author = author;
  this.isPublished = isPublished;
}
Document.prototype.publish = function publish() {
  this.isPublished = true;
};

//Class inherittance
function Book(title, author, topic) {
  Document.call(this, title, author, true);
  this.topic = topic;
}
Book.prototype = Object.create(Document.prototype);

En ES6 lo podemos hacer con una sintaxis más clara y explícita:

// ES6
//Class creation
class Document {
  constructor(title, author, isPublished) {
    this.title = title;
    this.author = author;
    this.isPublished = isPublished;
    }
  publish(){
        this.isPublished = true;
    }
}

//Class inherittance
class Book extends Document{
  constructor(title, author, topic){
    super(title, author, true);
    this.topic = topic;
  }
}

Módulos

Hasta ahora, los desarrolladores de Javascript habían cubierto su anhelo de modularizar el código gracias a librerías como RequireJS o browserify. ES6 incluye la funcionalidad de módulos, que nos permite exportar/importar objetos, funciones y clases desde código, sin tener que importarlos desde HTML.

Veamos como exportar un módulo, en el archivo lib/greetings.js:

// ES6
// lib/utils.js
module "utils" {
    export function greeting(name){
        console.log("Hi! " + name);
    }
}

Y como importarlo desde otro archivo JS:

// ES6
// app.js
import { greeting } from "utils";
var app = {
    welcome: function(){
        greeting("Mike");
    }
}
export app;

También podemos importar los módulos por su path, sin necesidad de definir explícitamente el módulo, así como definir un método defaultque se asigna a la variable que definimos a continuación del import:

// ES6
// lib/math.js
export function mult(a, b){
    return a*b;
}

export const PI = 3.141593;

export default function(a, b){
    return a + b;
}

Podemos encontrar documentación muy detallada al respecto. A continuación dejo las posibles formas de importar módulos:

// ES6
import defaultMember from "module-name";
import * as name from "module-name";
import { member } from "module-name";
import { member as alias } from "module-name";
import { member1 , member2 } from "module-name";
import { member1 , member2 as alias2 , [...] } from "module-name";
import defaultMember, { member [ , [...] ] } from "module-name";
import defaultMember, * as name from "module-name";
import "module-name";

Conclusiones

Estas son solo algunas de las novedades más características de ECMAScript 2015. En próximos posts iré desgranando más novedades.

En todo caso, puedes comprobar que ES6 supone una revolución en la forma de utilizar Javascript, y lo dota de algunos de mecanismos que llevan siendo reclamados por la comunidad desde hace tiempo. Podríamos decir que nos encontramos ante una versión más madura y potente de Javascript, y ha llegado para quedarse, así que NO lo dudes y empieza a utilizarlo en tu día a día, lo agradecerás.