ECMAScript 6
ECMAScript 2015 (6ta edición) es la versión actual de la especificación del lenguaje ECMAScript, conocida simplemente como ES6. El nombre clave es ES.next o Harmony. El primer borrador de la especificación (basada en ECMAScript 5.1) fue publicada el 12 de Julio de 2011 como ES.next, en Agosto de 2014 el borrador de la especificación fue detenido respecto de nuevas características y pasó por un período de estabilización y soluciones de bugs, finalmente, el ECMA-262 edición 6 fue oficialmente aprobada y publicada en Junio 17 de 2015 por el ECMA General Assembly. También aparecerá como el ISO/IEC 16262:2016. - ECMAScript 6 support in Mozilla
Puedes encontrar una tabla comparativa del nivel de soporte de ES6 en distintos entornos de ejecución, incluyendo los principales navegadores web (Desktop browsers), en http://kangax.github.io/compat-table/es6/.
Características
A continuación se plantean solo algunas pocas características de ECMAScript 6 que es posible adoptar en cualquiera de los principales navegadores web.
Práctica 01
Crea el directorio
$HOME/www/es601
, accede a él y crea el archivoindex.html
con el siguiente contenido:<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>es6 - 01</title> </head> <body> <h1>es6 - 01</h1> <canvas id="dona-canvas" width="400" height="200"></canvas> <script src="js/main.js" type="module"></script> </body> </html>
Crea los directorios
js
,js/lib
,js/lib/e
,js/lib/f
yjs/lib/f/g
:alumno@servidor:~/www/es601$ mkdir -p js js/lib js/lib/e js/lib/f js/lib/f/g
Crea los siguientes archivos con su correspondiente contenido:
js/main.js
import {imprimir_saludo as moduloa_saludar, modificar_estado as moduloa_modificar} from "./moduloa.js"; import {imprimir_saludo as moduloc_saludar, modificar_estado as moduloc_modificar} from "./moduloc.js"; let nombre = "Módulo Main"; let identificacion = "Soy " + nombre; const estado = {}; estado[nombre] = 10; let saludo = "¡Hola!. " + identificacion + "." console.log(saludo); moduloa_saludar(nombre) moduloa_modificar(estado); moduloc_saludar(nombre); moduloc_modificar(estado); console.table(estado); console.log("¡Adios!. " + "Se despide " + nombre + ".");
js/moduloa.js
export {imprimir_saludo, modificar_estado}; import {imprimir_saludo as modulob_saludar, modificar_estado as modulob_modificar} from "./modulob.js"; let nombre = "Módulo A"; let identificacion = "Soy " + nombre; let imprimir_saludo = function(sujeto) { let saludo = "¡Hola " + sujeto + "!. " + identificacion + "." console.log(saludo); modulob_saludar(nombre); imprimir_despedida(sujeto); }; let imprimir_despedida = function(sujeto) { console.log("¡Adios " + sujeto + "!. " + "Se despide " + nombre + "."); }; let modificar_estado = function(estado) { if (estado[nombre] === undefined) { estado[nombre] = 20; } else { estado[nombre] = 5 * estado[nombre]; } modulob_modificar(estado); };
js/modulob.js
export {imprimir_saludo, modificar_estado}; let nombre = "Módulo B"; let identificacion = "Soy " + nombre; let imprimir_saludo = function(sujeto) { let saludo = "¡Hola " + sujeto + "!. " + identificacion + "." console.log(saludo); imprimir_despedida(sujeto); }; let imprimir_despedida = function(sujeto) { console.log("¡Adios " + sujeto + "!. " + "Se despide " + nombre + "."); }; let modificar_estado = function(estado) { if (estado[nombre] === undefined) { estado[nombre] = 30; } else { estado[nombre] = 3 * estado[nombre]; } };
js/moduloc.js
export {imprimir_saludo, modificar_estado}; import {imprimir_saludo as modulod_saludar, modificar_estado as modulod_modificar} from "./lib/modulod.js"; let nombre = "Módulo C"; let identificacion = "Soy " + nombre; let imprimir_saludo = function(sujeto) { let saludo = "¡Hola " + sujeto + "!. " + identificacion + "." console.log(saludo); modulod_saludar(nombre); imprimir_despedida(sujeto); }; let imprimir_despedida = function(sujeto) { console.log("¡Adios " + sujeto + "!. " + "Se despide " + nombre + "."); }; let modificar_estado = function(estado) { estado[nombre] = 40; modulod_modificar(estado); };
js/lib/modulod.js
export {imprimir_saludo, modificar_estado}; import {imprimir_saludo as moduloe_saludar, modificar_estado as moduloe_modificar} from "./e/moduloe.js"; import "./Chart.js"; let nombre = "Módulo D"; let identificacion = "Soy " + nombre; let imprimir_saludo = function(sujeto) { let saludo = "¡Hola " + sujeto + "!. " + identificacion + "." console.log(saludo); usar_modulo_chart(); moduloe_saludar(nombre); imprimir_despedida(sujeto); }; let imprimir_despedida = function(sujeto) { console.log("¡Adios " + sujeto + "!. " + "Se despide " + nombre + "."); }; let usar_modulo_chart = function() { /* * Librería: http://www.chartjs.org * Fuente: http://www.chartjs.org/samples/latest/charts/doughnut.html */ let randomScalingFactor = function() { return Math.round(Math.random() * 100); }; let dona_canvas = document.getElementById("dona-canvas"); let dona_configuracion = { type: 'doughnut', data: { datasets: [{ data: [ randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), ], backgroundColor: [ "red", "orange", "yellow", "green", "blue", ], label: 'Dataset 1' }], labels: [ 'Red', 'Orange', 'Yellow', 'Green', 'Blue' ] }, options: { responsive: false, legend: { position: 'top', }, title: { display: true, text: 'Chart.js Doughnut Chart' }, animation: { animateScale: true, animateRotate: true } } }; let dona_grafica = new Chart(dona_canvas, dona_configuracion); }; let modificar_estado = function(estado) { estado[nombre] = 50; moduloe_modificar(estado); };
js/lib/e/moduloe.js
export {imprimir_saludo, modificar_estado}; import {imprimir_saludo as modulof_saludar, modificar_estado as modulof_modificar} from "../f/modulof.js"; import {modificar_estado as moduloa_modificar} from "../../moduloa.js"; let nombre = "Módulo E"; let identificacion = "Soy " + nombre; let imprimir_saludo = function(sujeto) { let saludo = "¡Hola " + sujeto + "!. " + identificacion + "." console.log(saludo); modulof_saludar(nombre); imprimir_despedida(sujeto); }; let imprimir_despedida = function(sujeto) { console.log("¡Adios " + sujeto + "!. " + "Se despide " + nombre + "."); }; let modificar_estado = function(estado) { estado[nombre] = 60; modulof_modificar(estado); moduloa_modificar(estado); };
js/lib/f/modulof.js
export {imprimir_saludo, modificar_estado}; import {imprimir_saludo as modulog_saludar, modificar_estado as moduloe_modificar} from "./g/modulog.js"; import {modificar_estado as modulob_modificar} from "../../modulob.js"; let nombre = "Módulo F"; let identificacion = "Soy " + nombre; let imprimir_saludo = function(sujeto) { let saludo = "¡Hola " + sujeto + "!. " + identificacion + "." console.log(saludo); modulog_saludar(nombre); imprimir_despedida(sujeto); }; let imprimir_despedida = function(sujeto) { console.log("¡Adios " + sujeto + "!. " + "Se despide " + nombre + "."); }; let modificar_estado = function(estado) { estado[nombre] = 70; moduloe_modificar(estado); modulob_modificar(estado); };
js/lib/f/g/modulog.js
export {imprimir_saludo, modificar_estado}; let nombre = "Módulo G"; let identificacion = "Soy " + nombre; let imprimir_saludo = function(sujeto) { let saludo = "¡Hola " + sujeto + "!. " + identificacion + "." console.log(saludo); imprimir_despedida(sujeto); }; let imprimir_despedida = function(sujeto) { console.log("¡Adios " + sujeto + "!. " + "Se despide " + nombre + "."); }; let modificar_estado = function(estado) { estado[nombre] = 80; };
Descarga la librería Chart.js:
alumno@servidor:~/www/es601 $ wget https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.js -P js/lib
Hasta este punto tendrás la siguiente estructura en
$HOME/www/es601
:alumno@servidor:~/www/es601 $ pwd && tree /home/alumno/www/es601 . ├── index.html └── js ├── lib │ ├── Chart.js │ ├── e │ │ └── moduloe.js │ ├── f │ │ ├── g │ │ │ └── modulog.js │ │ └── modulof.js │ └── modulod.js ├── main.js ├── moduloa.js ├── modulob.js └── moduloc.js 5 directories, 10 files
Inicia el servidor web y accede a él poniendo atención a lo impreso en Console:
Observaciones:
En
index.html
el uso del atributotype="module"
para el elemento script establece que el archivojs/main.js
es un módulo de ECMAScript.El módulo
js/main.js
mediante import requiere los módulosjs/moduloa.js
yjs/moduloc.js
. El navegador web se encargará de descargar estos módulos.Los módulos
js/moduloa.js
yjs/moduloc.js
mediante export exponen algo al exterior para quien lo requiera, en este caso el módulojs/main.js
.La gráfica de dependencia entre los módulos sería la siguiente:
La declaración e inicialización de variables en los módulos se lleva a cabo mediante let y const.
El módulo
js/main.js
declara e inicializa la variableestado
medianteconst
, la cual es dada como argumento a las funciones expuestas por los demás módulos.Proyectos para modularizar el código de ECMAScript:
Proyectos para el empaquetado (bundle) de módulos-dependencias en ECMAScript:
Al respecto: ¿Qué es un JavaScript Bundle? y WebPack y la gestión de dependencias en JavaScript.
Práctica 02
Crea el directorio
$HOME/www/es602
, accede a él y crea el archivoindex.html
con el siguiente contenido:<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>es6 - 02</title> </head> <body> <h1>es6 - 02</h1> <script type="module" src="js/main.js"></script> </body> </html>
Crea los directorios
js
,js/clases
yjs/lib
:alumno@servidor:~/www/es602$ mkdir -p js js/clases js/lib
Crea los siguientes archivos con su correspondiente contenido:
js/main.js
/* * Se requiere (`import`) de todo aquello expuesto (`export`) por el módulo 'es6.js'. * Localmente, aquello expuesto (`export`) por 'es6.js', es precedido por el sobrenombre (`as`) 'es6'. */ import * as es6 from "./lib/es6.js"; let main = function() { console.log("Iniciando..."); console.group("Uso de let y const"); es6.uso_de_let_y_const(); console.groupEnd(); console.group("Uso de `template string` o `template literals`"); es6.uso_de_template_string_literal("MUNDO"); console.groupEnd(); console.group("Uso de 'arrow function' o 'función flecha'"); es6.uso_de_arrow_function(); console.groupEnd(); console.group("Uso de argumentos opcionales"); es6.uso_de_argumentos_opcionales("MUNDO"); es6.uso_de_argumentos_opcionales("MUNDO", "PLANETA"); es6.uso_de_argumentos_opcionales("MUNDO", "PLANETA", "TIERRA"); console.groupEnd(); console.group("Uso de desestructuración"); es6.uso_de_desestructuracion(-15); console.groupEnd(); console.group("Uso de maps y sets"); es6.uso_de_maps_y_sets(-3, 3); console.groupEnd(); console.group("Uso de clases"); es6.uso_de_clases(); console.groupEnd(); console.log("Terminado"); }; main();
js/lib/es6.js
import {ClaseA} from "../clases/clasea.js"; import {ClaseB} from "../clases/claseb.js"; import {ClaseC} from "../clases/clasec.js"; /* * Todo aquello precedido por `export` (declaración de variable, definición de función) queda * expuesto al exterior por quien requiera (`import`) este módulo. */ export let uso_de_let_y_const = function() { // Declaraciones de a y b internas (o locales) en todo este bloque let a = 10; let b = { "x": a, "y": 2 * a }; // Declaración de una variable, impide su re-declaración o re-asignación // pero no la modificación del contenido de un objeto-arreglo-referencia. const c = []; imprimir_variable("a", a); imprimir_variable("b", b); imprimir_variable("c", c); if (true) { // Ocultan las declaraciones de a y b externas a este bloque let a = 30; let b = { "x": a, "y": 2 * a }; imprimir_variable("a", a); imprimir_variable("b", b); // La variable c es externa a este bloque imprimir_variable("c", c); if (true) { // Ocultan las declaraciones de a, b y c externas a este bloque let a = 101; let b = 104; const c = [100]; for (let i = a; i <= b; i++) { // La variable i es local a este bloque c.push(i); } c.push(105); imprimir_variable("a", a); imprimir_variable("b", b); imprimir_variable("c", c); } for (let i = 1; i <= 5; i++) { // La variable i es local a este bloque // La variable c es externa a este bloque c.push(3 * i); } } imprimir_variable("a", a); imprimir_variable("b", b); imprimir_variable("c", c); }; export let uso_de_template_string_literal = function(sujeto) { let saludo = `¡Hola ${sujeto}!`; let palabras = ["¡", "Feliz", (new Date).getFullYear(), "!"]; let felicitacion = `${palabras[0]}${palabras[1]} ${palabras[2]}${palabras[3]}`; let despedida = ` ♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜ ¡Adios ${sujeto}! ♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟ `; imprimir_variable("saludo", saludo); imprimir_variable("felicitacion", felicitacion); imprimir_variable("despedida", despedida); }; export let uso_de_arrow_function = function() { const X = 3; // Declaración de una función (x como parámetro) let a = ((x) => { let y = x <= 0 ? x * -1 : x; return [(y -1), y, (y + 1)]; }); imprimir_variable("a", a); // Aplicación de la función let b = a(-10); let c = a(10); imprimir_variable("b", b); imprimir_variable("c", c); // Declaración de una función (sin parámetro) let d = (() => { let a = []; for (let i = 0; i < X; i++) { a.push(Math.floor(Math.random() * 100)); } return a; }); imprimir_variable("d", d); // Aplicación de la función let e = d(); imprimir_variable("e", e); }; export let uso_de_argumentos_opcionales = function(nombre = "N/A", apellido_paterno = "N/A", apellido_materno = "N/A") { // Los argumentos dados en los parámetros son opcionales. De no ser dados al momento de aplicar a la función los // valores que tendrán serán de "N/A" dentro de la función. let a = { "id": (Math.floor(Math.random() * 100)), "nombre": nombre, "apellidos": { "paterno": apellido_paterno, "materno": apellido_materno }, registro: (new Date).toISOString() }; imprimir_variable("a", a); }; export let uso_de_desestructuracion = function(numero_positivo) { let x = numero_positivo > 0? numero_positivo : numero_positivo * -1; let a = [(x - 1), x, (x + 1)]; /* * Declaración e inicialización de las variables b, c y d mediante la desestructuración del arreglo a */ let [b,c,d] = a; imprimir_variable("a", a); imprimir_variable("b", b); imprimir_variable("c", c); imprimir_variable("d", d); let e = { "f": (x - 1), "g": x, "h": (x + 1) }; /* * Declaración e inicialización de las variables f, g y h mediante la desestructuración del objeto e */ let {f,g,h} = e; imprimir_variable("e", e); imprimir_variable("f", f); imprimir_variable("g", g); imprimir_variable("h", h); }; export let uso_de_maps_y_sets = function(numero_negativo, numero_positivo) { let a = new Map(); a.set("x", 10); a.set("y", true); a.set("z", "¡Hola mundo!"); imprimir_variable("a", a); let b = new Map(); b.set(10, numero_negativo); b.set(20, numero_positivo); b.set(30, numero_negativo); b.set(40, numero_positivo); imprimir_variable("b", b); let c = new Map([["nombre", "MUNDO"], ["apellido_paterno", "PLANETA"], ["apellido_materno", "TIERRA"]]); imprimir_variable("c", c); let d = new Set(); d.add(10); d.add(true); d.add("¡Hola mundo!"); imprimir_variable("d", d); let e = new Set(); e.add(numero_negativo); e.add(numero_positivo); e.add(numero_negativo); e.add(numero_positivo); imprimir_variable("e", e); let f = new Set([numero_negativo, 2, 1, 0, 1, 2, numero_positivo]); imprimir_variable("f", f); let g = new Set([numero_negativo, numero_positivo, numero_negativo, numero_positivo, numero_negativo, numero_positivo]); imprimir_variable("g", g); }; export let uso_de_clases = function() { let objA1 = new ClaseA(); imprimir_variable("objA1", objA1); imprimir_variable("objA1.toString()", objA1.toString()); let objB1 = new ClaseB(); for (let i = 0; i < 5; i++) { imprimir_variable("objB1.b()", objB1.b()); } let objC1 = new ClaseC(); imprimir_variable("objC1.c()", objC1.c()); }; /* * Al no ser precedido por `export` se concidera local al módulo y solo disponible dentro del mismo. */ let imprimir_variable = function(nombre, valor) { console.groupCollapsed(nombre); console.log(valor); console.groupEnd(); };
js/clases/clasea.js
export class ClaseA { constructor() { this.x = 10; this.y = "¡Hola mundo!"; this.z = false; } toString() { return "ClaseA {" .concat(`x: ${this.x}, `) .concat(`y: ${this.y}, `) .concat(`z: ${this.z} `) .concat("}"); } }
js/clases/claseb.js
export class ClaseB { constructor(x = 7, y = 9) { this.i = 0; /* * Se hace uso de la clase local ClaseA, no de la encontrada en el módulo clasea.js */ this.objA = new ClaseA(x, y); } b() { if (this.i === this.objA.a2()) { this.i = 0; } let i = this.i; this.i += 1; return this.objA.a3(i); } } /* * Al no estar precedida por `export` se considera una clase local al módulo */ class ClaseA { constructor(x, y) { this.i = x; this.j = y; this.as = []; this.a1(); } a1() { for (let i = this.i; i <= this.j; i++) { this.as.push(3 * i); } } a2() { return this.as.length; } a3(i) { return this.as[i]; } }
js/clases/clasec.js
import {ClaseB} from "./claseb.js"; export class ClaseC { constructor() { this.objB = new ClaseB(2, 4); } c() { let t = 0; for (let i = 0; i < 6; i++) { t += this.objB.b(); } return t; } }
Hasta este punto tendrás la siguiente estructura en
$HOME/www/es602
:alumno@servidor:~/www/es602 $ pwd && tree /home/alumno/www/es602 . ├── index.html └── js ├── clases │ ├── clasea.js │ ├── claseb.js │ └── clasec.js ├── lib │ └── es6.js └── main.js 3 directories, 6 files
Inicia el servidor web y accede a él poniendo atención a lo impreso en Console:
Observaciones:
- Las nuevas características de ECMAScript 6 presentes son: let & const, string literals, arrow functions, optional arguments, destructuring, maps & sets y classes.
- Las nuevas características de ECMAScript 6 no presentes son: symbols, proxies, generators y promises.
La gráfica de dependencia entre los módulos sería la siguiente:
Lecturas
- ECMAScript 6 en Mozilla Hispano: 1 y 2
- Aprende ES6, JavaScript Moderno (PDF) por Enrique Oriol
- ES6. El remozado JavaScript (PDF) por Juan Antonio Jiménez Torres
- ES6 Features por Ralf S. Engelschall
- Exploring ES6 por Dr. Axel Rauschmayer