ECMAScript 2015 (también conocido como ES.next, Harmony, ECMAScript 6 o ES6) es la sexta edición del lenguaje ECMAScript y fue aprobada y publicada por el ECMA General Assembly el 17 de junio de 2015. Al ser JavaScript una implementación del estándar ECMAScript, si eres desarrollador JavaScript, deberías interesarte por el nuevo estándar.
No estamos hablando de algo que se implementará en el futuro, sino que es algo que está aprobado hace más de dos años y que puedes empezar a utilizar en la mayoría de tus proyectos desde hace bastante tiempo.
¿Por qué aprender ECMAScript 2015?
Quizás te estés preguntando por qué deberías aprender ECMAScript 2015 desde ahora si el código JavaScript que escribes actualmente es (y será) compatible con los navegadores por muchos años. Pues bien, en mi opinión debes aprenderlo, no solo para empezar a escribir un código que en muchos aspectos será más robusto, extensible y preparado para el futuro, sino también porque muchos frameworks basados en JavaScript están adoptando el nuevo estándar (dígase React, Redux o Angular por poner algunos ejemplos) y en un futuro se te hará más difícil adoptar un nuevo framework si no dominas muchas de las características del lenguaje que utilizan.
Si te estás preguntando cómo podrías escribir código en el nuevo estándar y que sea soportado por la mayoría de los navegadores, te diré que la mayoría de los navegadores implementan ya una buena parte del nuevo estándar, y las cosas que no son soportadas, pueden ser solventadas con polyfills o con herramientas más robustas como Babel, que te ayudan a transpilar tu código de ECMAScript 2015 a un estándar anterior.
En este artículo intentaré reunir algunas de las principales mejoras que ha introducido ECMAScript 2015, creo que mostrando estas mejoras te convencerás de que no es muy difícil adoptar el nuevo estándar.
Nuevos métodos
ECMAScript 2015 ha añadido nuevos métodos a los objetos Object, Array, Math y Number así como nuevos métodos en cadenas de texto.
Método assign del objeto Object
El método assign del objeto Object copia los valores de todas las propiedades enumerables de uno o más objetos a un objeto de destino, su implementación es muy sencilla:
var datos = {id: 100, total: 2500};
var colores = {color: "#FFFFFF", background: "#CCCCCC"};
var estilos = {height: "200px", width: "100px", color: "#FF0000"};
Object.assign(datos, colores, estilos);
console.log(datos);
// {id: 100, total: 2500, color: "#FF0000", background: "#CCCCCC", height: "200px", width: "100px"}
Nótese cómo la propiedad color del objeto colores es sobrescrita por la del objeto estilos.
Método findIndex y find del objeto Array
Estos dos métodos trabajan de manera similar (ninguno de los dos transforma el Array). El primero, findIndex, se utiliza para encontrar el primer índice en el Array que cumpla con una condición específica. Mientras que el segundo método, find, se utiliza para encontrar el primer elemento en sí que cumpla dicha condición. La forma de chequear cada elemento del Array para verificar si cumple la condición, es mediante una función que es enviada al método:
var array = [1, 6, 2, 7, 3, 8, 4, 9, 5];
var fi = array.findIndex(function (n) {
return n * n > 40;
});
console.log(fi); // 3 (la posición 3 corresponde al número 7. 7^2 = 49)
var f = array.find(function (n) {
return n % 4 === 0;
});
console.log(f); // 8 (es el primer número divisible entre cuatro en el Array)
Nuevos métodos del objeto Math
El objeto Math cuenta ahora con nuevos métodos:
Math.trunc(12.5); // 12
Math.trunc(-12.5); // -12
Math.sign(10); // 1
Math.sign(-10); // -1
Math.sign(0); // 0
Math.sign(-0); // -0
Math.sign(NaN); // NaN
Nuevos métodos del objeto Number
El objeto Number cuenta ahora con nuevos métodos que nos permitirán trabajar con números de una manera más poetente.
var big = Math.pow(10, 9);
Number.isSafeInteger(big); // true
Number.isSafeInteger(big * big); // false
Number.isNaN(10); // false
Number.isNaN(NaN); // true
Number.isNaN(0/0); // true
Number.isNaN("cien"); // false
Number.isNaN("100"); // false
Number.isNaN({}); // false
Number.isNaN(undefined); // false
Number.isNaN("NaN"); // false
Number.isFinite(-Infinity); // false
Number.isFinite(Infinity); // false
Number.isFinite(big); // true
Number.isFinite(NaN); // false
Nuevos métodos en cadenas de texto
ECMAScript 2015 incluye nuevos métodos muy interesantes para trabajar con las cadenas de texto. Aunque algunos de estos métodos se podían emular hasta ahora usando indexOf y lastIndexOf, ahora contamos con métodos nativos para esos propósitos:
var cadena = "ECMAScript 2015 en xprimiendo";
cadena.startsWith("ECMAScript"); // true
cadena.startsWith("ECMAScript", 5); // false
cadena.startsWith("2015", 11); // true
cadena.endsWith("xprimiendo"); // true
cadena.endsWith("xprimiendo", 10); // false
cadena.endsWith("2015", 15); // true
cadena.includes("en "); // true
cadena.includes("en ", 17); // false
cadena.includes("primi", 10); // true
cadena.repeat(2); // ECMAScript 2015 en xprimiendoECMAScript 2015 en xprimiendo
Variables de ámbito de bloque
Hasta ahora, la manera de declarar una varible en JavaScript era con la sentencia var. Todo el que domina este lenguaje sabe que el ámbito de una variable declarada de esta manera es la función que la contiene (exceptuando las variables declaradas fuera de función alguna que son de alcance global).
ECMAScript 2015 introduce let y const, las cuales se comportan de manera diferente a la manera tradicional que teníamos de declarar una variable.
Uso de let
Analizemos el siguiente código:
var es5 = true;
if (es5) {
var mensaje = "ES5 es true";
}
console.log(mensaje);
En el código anterior, lo mismo es5 que mensaje son variables declaradas fuera de una función, por lo que su ámbito será global. La variable mensaje, sin importar que se haya declarado dentro del bloque de una condición, sigue siendo accesible desde fuera de dicho bloque. Con ECMAScript 2015 esta situación cambia drásticamente:
let es6 = true;
if (es6) {
let mensaje = "ES6 es true";
}
console.log(mensaje);
El anterior código lanzará un error, ya que se está intentando acceder a una variable no definida. La variable mensaje se ha declarado dentro del bloque de una condición usando let, por lo tanto su ámbito estará circunscrito a dicho bloque, fuera del bloque esta variable no estará definida.
Observemos este otro ejemplo:
for (var i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i);
}, 100);
}
El anterior código crea un ciclo de 0 hasta 9 y en cada iteración crea un setTimeout con una función que lo que hace es simplemente lanzar el valor de la variable i en la consola. Quien no tenga mucha experiencia en JavaScript podría pensar que el resultado final serían números del 0 al 9 impresos en la consola. Pero quien conozca bien este lenguaje y el alcance de las variables, sabrá que la variable i es global y solo cambia su valor en cada iteración del ciclo, por lo que al culminar todos los setTimeout el valor de la misma será el último valor adquirido, en este caso 10. Por lo tanto, el resultado será el número 10 impreso en la consola 10 veces.
Veamos cómo se comporta el mismo código anterior usando let para declarar la variable i:
for (let i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i);
}, 100);
}
En el anterior código, al haberse declarado la variable i con let, el alcance de la misma se circunscribe al bloque conformado por el ciclo. Por lo tanto, en cada iteración se utiliza una instancia diferente de la variable i, dando como resultado números del 0 al 9 impresos en la consola.
Uso de const
Con respecto a let, las variables declaradas con const tienen también un ámbito de bloque. Pero al ser constantes, su principal característica es que no se pueden redeclarar ni se les puede reasignar otro valor. Tampoco pueden compartir nombre con otra función o con otra variable en su mismo ámbito. Por ejemplo, analizemos el siguiente código:
const MAX_SPEED = 100;
/* CUALQUIERA DE LAS SIGUIENTES LINEAS LANZARÁ UN ERROR */
MAX_SPEED = 120; // Uncaught TypeError: Assignment to constant variable.
MAX_SPEED += 20; // Uncaught TypeError: Assignment to constant variable
let MAX_SPEED = 120; // Uncaught SyntaxError: Identifier 'MAX_SPEED' has already been declared
function MAX_SPEED () { return 120; } // Uncaught SyntaxError: Identifier 'MAX_SPEED' has already been declared
Como podemos observar, el uso de constantes nos pueden ayudar bastante cuando queremos crear una referencia a la variable de solo lectura. Esto hará nuestro código más robusto e impedirá que algun error o cambio futuro en el código, redeclare o reasigne otro valor a esas variables.
Hay que tener en cuenta que esto no significa inmutabilidad. Declarar una variable con const no permitirá que el valor de la variable sea reasignado, pero por ejemplo, si la variable declarada con este método es un objeto, este último puede ser variado sin problemas. Observemos el siguiente código:
const DATOS = {id: 100, valor: 35};
const IDS = [100, 200, 300, 400];
/* INTENTAR REASIGNAR EL VALOR (ERROR) */
DATOS = {id: 100, valor: 35, hash: '45sab3cf'}; // (index):49 Uncaught TypeError: Assignment to constant variable
IDS = [100, 200, 300, 400, 500]; // Uncaught TypeError: Assignment to constant variable
/* VARIAR EL VALOR (NINGÚN ERROR) */
DATOS.hash = '45sab3cf';
IDS.push(500);
Creación de nuevos ámbitos de bloque
Como hemos visto, al crear variables con let o const, estas se circunscriben al bloque donde han sido declaradas. Pero ¿qué entendemos como bloque? Con ECMAScript 2015 todo lo que se encuentre entre llaves es un bloque de código, por lo que la siguiente expresión {{}} es válida por sí sola. Observermos el siguiente código:
{
let a = 10;
{
let a = 20;
console.log(a); // imprimirá 20
}
console.log(a); // imprimirá 10
}
Los bloques de código pueden ser creados también para declarar funciones, por lo que el hoisting de las mismas se realizará dentro de dicho bloque de código. Observemos el siguiente ejemplo:
{
console.log( calcula(2, 3) ); // imprimirá 5
function calcula (a, b) { return a + b; }
{
console.log( calcula(2, 3) ); // imprimirá 6
function calcula (a, b) { return a * b; }
console.log( calcula(5, 4) ); // imprimirá 20
}
console.log( calcula(5, 4) ); // imprimirá 9
}
Con la llegada de los bloques, ya no hay necesidad de utilizar las IIFE solo para crear un nuevo ámbito de código.
Plantillas de cadenas de texto
Las plantillas de cadenas de texto en ECMAScript 2015 son creadas usando acentos graves. Dichas plantillas nos brindan múltiples posibilidades al trabajar con cadenas de texto, recorramos algunas de estas:
Cadenas de texto multilínea
Una de las posibilidades que nos brinda trabajar con plantillas de cadenas de texto, es escribir texto multilínea dentro del código JavaScript, sin tener que añadir \n al final de cada línea, ni escapar la nueva línea con un caracter \ o concatener las líneas usando el operador +. Todo es tan sencillo como el siguiente ejemplo:
let mensaje = `Este texto contiene
más de una línea y para lograrlo
solo hay que usar acentos graves
en vez de comillas en nuestro código`;
Interpolación de variables y expresiones
Ya no es necesario usar el operador + si deseamos concatenar cadenas de textos con variables o expresiones. Tan solo tenemos que usar la siguiente sintaxis para situar una variable o una expresión dentro del texto: ${variable o expresión}. Observemos el siguiente ejemplo:
let coche = "Tesla Model S";
let velocidad = 180;
let horas = 2;
let mensaje = `Un ${coche} tiene una velocidad promedio
de ${velocidad}Km/h. Por lo tanto en ${horas} horas
puede recorrer ${velocidad * horas} kilómetros`;
console.log(mensaje);
El anterior código lanzará el siguiente mensaje en la consola:
Un Tesla Model S tiene una velocidad promedio
de 180Km/h. Por lo tanto en 2 horas
puede recorrer 360 kilómetros
Uso de funciones de etiquetado
Si quisiéramos usar una función para hacer un postprocesado de una plantilla de cadena de texto y devolver una nueva cadena, lo podríamos lograr usando el llamado etiquetado de plantillas. Observemos un ejemplo de una función preparada para procesar una plantilla en particular y el resultado del postprocesado:
let nombre;
let nota;
let mensaje;
// La función recibirá como primer parámetro un array
// con todas las cadenas contenidas en la plantilla
// el segundo, tercero y hasta n parámetros serán las variables
// o expresiones contenidas en la plantilla
function post (cadenas, nombre, nota) {
let cad0 = cadenas[0]; // cadena vacía porque las plantillas utilizadas en los ejemplos comienzan por una variable
let cad1 = cadenas[1];
let cad2 = cadenas[2];
let final = `porque${cad1}${nota}${cad2}`;
if (nota < 5) {
return `${nombre} suspendió ${final}`;
} else if (nota < 7) {
return `${nombre} está aprobado ${final}`;
} else if (nota < 9) {
return `${nombre} tiene una nota notable ${final}`;
} else if (nota < 10) {
return `${nombre} tiene una nota sobresaliente ${final}`;
} else if (nota === 10) {
return `${nombre} tiene matrícula de honor ${final}`;
}
return `No se reconoce la nota de ${nombre}`;
}
nombre = "Julio";
nota = 3;
mensaje = post`${nombre} obtuvo ${nota} puntos en el examen`;
console.log(mensaje);
// Julio suspendió porque obtuvo 3 puntos en el examen
nombre = "Carmen";
nota = 7.5;
mensaje = post`${nombre} sacó ${nota} de nota en el examen`;
console.log(mensaje);
// Carmen tiene una nota notable porque sacó 7.5 de nota en el examen
nombre = "José";
nota = 10;
mensaje = post`${nombre} ha obtenido ${nota} puntos en el examen final de curso`;
console.log(mensaje);
// José tiene matrícula de honor porque ha obtenido 10 puntos en el examen final de curso
Operador de propagación
El operador de propagación se representa con tres puntos. Este operador permite expandir los elementos contenidos en un Array. Los siguientes ejemplos muestran ejemplos específicos del uso de este operador:
ECMAScript 5 | ECMAScript 2015 |
---|---|
|
|
|
|
|
|
|
|
|
|
Asignación por desestructuración
Esta expresión permite extraer independientemente los valores de objetos de tipo Array u Object para declarar con ellos variables. Los siguientes ejemplos muestran ejemplos específicos del uso de esta sintaxis:
ECMAScript 5 | ECMAScript 2015 |
---|---|
|
|
|
|
|
|
|
|
Mejoras en la declaración de propiedades de objetos
ECMAScript 2015 ha mejorado la manera de declarar las propiedades de los objetos, haciendo que el código sea más fácil de leer y menos repetitivo.
Sintaxis abreviada para propiedades de objetos
let a = 1;
let b = 2;
let c = 3;
let obj = {a, b, c}; // {a: 1, b: 2, c: 3}
Propiedades dinámicas en la declaración de objetos
let prefix = "prop";
let obj = {
[`${prefix}1`]: "uno",
[`${prefix}2`]: "dos",
[`${prefix}3`]: "tres",
}; // {prop1: "uno", prop2: "dos", prop3: "tres"}
Sintaxis abreviada para la declaración de métodos
let obj = {
sum (a, b) { return a + b; },
rest (a, b) { return a - b; },
mult (a, b) { return a * b; },
div (a, b) { return a / b; }
}
console.log( obj.mult(2, 5) ); // 10
Funciones flecha
Las funciones flecha no son más que una nueva expresión con una sintaxis más corta que una expresión de función convencional. Este tipo de funciones son siempre anónimas, carecen de su propio this o arguments y no contienen una propiedad prototype.
Este tipo de función no puede ser usada para crear un constructor y no es la función más indicada para usar como método de un objeto ya que las referencias a this, en muchos casos no harían referencia a dicho objeto.
Para comprender mejor cómo implementar una función flecha, observemos la siguiente tabla:
ECMAScript 5 | ECMAScript 2015 |
---|---|
|
|
|
|
|
|
|
|
|
|
Al carecer las funciones flecha de su propio this, son muy útiles para tomar el this del contexto en el que están creadas. Esto evita tener que crear una variable adicional para acceder a esta propiedad o utilizar el método bind:
ECMAScript 5 | ECMAScript 2015 |
---|---|
|
|
|
|
Parámetros por defecto en las funciones
Con ECMAScript 2015 podemos declarar el valor por defecto que adquirirá el parámetro de una función si este no se envía o su valor es undefined. Esto nos evita el tener que redeclarar la variable al inicio de la función si su valor es undefined (lo cual había sido una práctica habitual cuando se quería dar valores por defecto a parámetros opcionales de una función):
function multiplicar (array, multiplo = 1) {
return array.map(n => n * multiplo);
}
console.log( multiplicar([1, 2, 3, 4, 5], 2) ); // [2, 4, 6, 8, 10]
console.log( multiplicar([1, 2, 3, 4, 5]) ); // [1, 2, 3, 4, 5]
Parámetros rest
Los parámetros rest nos permiten tratar un número indefinido de parámetros enviados a una función como un array. Solo debemos anteponer operador de propagación al parámetro de la función y este será tratado como un Array:
function impares (...numeros) {
return numeros.filter(n => n % 2 !== 0);
}
console.log( impares(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) ); // [1, 3, 5, 7, 9]
Utilizar los parámetros rest nos evitan tener que hacer un slice del objeto arguments si queremos acceder al resto indefinido de parámetros enviados a una función:
ECMAScript 5 | ECMAScript 2015 |
---|---|
|
|
Y mucho más…
Esto es solo una pequeña muestra de todas las mejoras que trae ECMAScript 2015. Podríamos escribir un infinito artículo con todas las mejoras que no hemos mencionado, entre ellas, promesas, clases, módulos, símbolos, iterables, generadores, por mencionar solo algunas de ellas. El propósito de este artículo es despertar la curiosidad de quien no se ha decidido todavía en aprender ECMAScript 2015. Ojalá lo haya logrado. Espero que este artículo te haya servido de ayuda en tu aprendizaje.
Puedes situar fragmentos de código dentro de etiquetas <pre></pre> y código HTML o XML entre etiquetas <xmp></xmp>.