ECMAScript 2015
con node.js
+GonzaloRuizdeVilla
@gruizdevilla
Node.js Madrid & other places
Noviembre 2014
About me:
Socio fundador de Adesis Netlife @adesis
Google Developer Expert en Angular
ECMAScript
a.k.a. su implementación como JavaScript
Brendan Eich inventa JavaScript 1.0
Inspiración: Scheme -> funciones Self -> prototipos Java -> sintaxis (ouch!)
1995
ECMAScript 3 (nuevo nombre para complacer a todos: JScript vs JavaScript)
1999
...ECMAScript 4 (en 2008 se abandona, antes de nacer)
ECMAScript 5 (mejoras varias al lenguaje, "option strict")
2009
ECMAScript 2015/ES6 (Junio 2015)
2015
ECMAScript 2016/7 (en progreso...)
???
¿Qué aporta ES6?
Cuando ves otro lenguaje y ...
Algunas características nuevas
- (fat) arrow functions
- Const
- Let
- Default function parameters
- Rest parameters
- Spread
- class
- super
- for of
- generators
- Map
- Set
- desestructuración
- ...
node.js soporta ES6
Pero hay que usar flags....
--harmony
--harmony_generators
--harmony_collections
--harmony_proxies
--harmony_modules
--harmony_scoping
--harmony_observation
--harmony_symbols
--harmony_iteration
no npm publish
No tenemos garantías de que quien use nuestro módulo use esos flags (experimentales por otra parte).
Transpilers
Source to source compilers
Programamos con ES6 y generamos ES5
El transpiler tiene las intrínsecas limitaciones del lenguaje destino: por ejemplo, WeakMap o WeakSetp
Hay otras opciones:
- babeljs, 6to5
- esnext
- Google Closure Compiler
- JsTransform
- TypeScript 1.5 beta
- y más
gulp.task('traceur', function () {
return gulp.src(path.srcAndTest)
.pipe(traceur({
sourceMap: true,
blockBinding: true
}))
.pipe(gulp.dest('dist'));
});
Tarea de gulp.js
(Fat)
Arrow
Functions
var suma = (a, b) => a + b;
expect( suma(1,2) ).toBe(3);
expect( [1, 2, 3].reduce(suma) ).toBe(6);
expect( [1, 2, 3].map(()=> 1) ).toEqual([1, 1, 1]);
expect( [1, 2, 3].map((x)=> x + 1) ).toEqual([2, 3, 4]);
expect( [1, 2, 3].map( x => x + 1) ).toEqual([2, 3, 4]);
El cuerpo puede ser:
- Una expresión. Se devuelve el valor.
- Una secuencia de declaraciones entre llaves. Hay que devolver valor de forma explícita.
//declaraciones
expect( [1, 2, 3].map(
x => {
var y = x + 1;
return y * 2;
}
)).toEqual([4, 6, 8]);
//una expresion
expect( [1, 2, 3].map(
(x, y) => (y = x +1, y * 2)
)).toEqual([4, 6, 8]);
Lexical this
Bye, bye bind! (al menos en algunos casos)
function Person() {
this.age = 0;
this.grow = () => this.age++;
}
var pedro = new Person();
var grow = pedro.grow;
for(var i = 0; i < 10; i++){
grow();
}
expect(pedro.age).toBe(10);
Block scoped binding
Quiz
¿Cual es la salida por consola?
var fns = [];
for(var i = 0; i <10; i++){
fns.push(function (){
console.log(i);
});
}
fns.forEach(x => x())
¿Y ahora?
var fns = [];
for(let i = 0; i <10; i++){
fns.push(function (){
console.log(i);
});
}
fns.forEach(x => x())
const para constantes
// intercambiando valores sin hacer "leaking"
// de variables auxiliares
{
const tmp = a;
a = b;
b = tmp;
}
comprehensions
comprehensions:
Como construir un array fácilmente a partir de otro(s)
(Esto se va para 2016 como mínimo...)
forma simple
var array = [for (x of [0, 1, 2]) x * 2];
expect(array).toEqual([0, 2, 4]);
var ABC = ["A", "B", "C"];
var abc = [for (x of ABC) x.toLowerCase()];
expect(abc).toEqual(["a", "b", "c"]) ;
forma combinada
var nombres = ["Ana", "Elena", "Angel"];
var apellidos = ["Gomez", "Perez"];
var pax = [for (x of nombres) for (y of apellidos) x + " " + y];
expect(pax).toEqual([
"Ana Gomez",
"Ana Perez",
"Elena Gomez",
"Elena Perez",
"Angel Gomez",
"Angel Perez"
]);
como crear matrices con dos vectores
var vector1 = [ 1, 2, 3 ];
var vector2 = [ 3, 5, 7 ];
var matrix = [for (i of vector1) [for (j of vector2) i * j]];
expect(matrix).toEqual([
[3, 5, 7],
[6, 10, 14],
[9, 15, 21]
]);
comprensiones con guardias
a.k.a. compresiones con ifs
var years = [ 1954, 1974, 1990, 2006, 2010, 2014 ];
var twoDigitYear = [for (year of years) if (year > 2000) year - 2000];
expect(twoDigitYear).toEqual([6, 10 ,14]);
resumen de comprensiones
[for (x of iterable) x]
[for (x of iterable) if (condition) x]
[for (x of iterable) for (y of iterable) x + y]
for of
Lo que hemos visto es el uso de un iterador que recorre un contenedor.
Podemos definir los nuestros propios.
a mano (con un objeto que devuelva Symbol.iterator)
function pares(max) {
return {
[Symbol.iterator]: function() {
var current = 0;
return {
next: function() {
current += 2;
var end = max < current;
return {
value: end ? undefined : current,
done: end
};
}
};
}
};
}
expect([for (x of pares(10)) x]).toEqual([2,4,6,8,10])
con generadores es más compacto
Y puedes poner varios "yield" dentro de la función
function* pares(max) {
var current = 2;
while(!(max < current)){
yield current;
current +=2;
}
}
expect([for (x of pares(10)) x]).toEqual([2,4,6,8,10])
Y se pueden evaluar a lo "lazy"
pares() es ahora infinito
function* pares() {
var current = 2;
while(true){
yield current;
current +=2;
}
}
// no hemos definido máximo, la lista es infinita
var res = (for (par of pares()) par);
expect(res.next().value).toBe(2)
expect(res.next().value).toBe(4)
take some
trabajando con iterables infinitos (o no...)
function* take(iterable, number){
var count = 0;
for(var taken of iterable){
if (++count >number){
return;
}
yield taken;
}
}
expect([for (x of take(pares(),5)) x])
.toEqual([2,4,6,8,10]);
Strings
el poder del back tick: `
¡multilínea!
expect(`esto es un
string que ocupa
varias lineas`)
.toEqual('esto es un\nstring que ocupa\nvarias lineas');
el poder del back tick: `
¡templates!
var a = 1;
var b = 2;
expect(`${a} + ${b} = ${a+b}`).toEqual('1 + 2 = 3');
Destructuring
"Desestructurando" listas
var [uno,,tres] = [1,2,3];
expect(uno + tres).toEqual(4);
var [a, [b, c], [d]] = ['hola', [', ', 'ejem'], ['mundo']];
expect(a + b + d).toEqual('hola, mundo');
"Desestructurando" objetos
function getPersona(){
return {
nombre: 'Sarah',
apellido: 'Palmer',
edad: 38,
email: 'sara@palmer.me'
}
}
var {nombre, email} = getPersona();
expect(nombre).toEqual('Sarah');
expect(email).toEqual('sara@palmer.me');
"Desestructurando" everywhere
function printName({nombre, apellido}){
return nombre + " " + apellido;
}
var printedName = printName(getPersona());
expect(printedName).toEqual("Sarah Palmer");
"Desestructurando" everywhere
var personas = [
{nombre: 'Pedro', apellido: 'Palmer'},
{nombre: 'Pepa', apellido: 'Palmer'},
{nombre: 'Paula', apellido: 'Palmer'},
{nombre: 'Pablo', apellido: 'Palmer'}
]
var nombres = personas.map(({nombre}) => nombre);
expect(nombres).toEqual(['Pedro', 'Pepa', 'Paula', 'Pablo'])
"Desestructurando" everywhere
var arr = [
{x:1, y:2},
{x:2, y:3}
];
var arr2 = arr.map(({x,y}) => x + y);
expect(arr2).toEqual([3,5]);
"Desestructurando" everywhere
var point = new Singularity(0,1,0, {mass:10});
var {x,y,radius, theta, properties: {mass}} = point;
expect(x).toEqual(0);
expect(y).toEqual(1);
expect(radius).toEqual(1);
expect(theta).toEqual(Math.PI/2);
expect(mass).toEqual(10);
Literales de objetos
Para ir un describir las cosas un poco más rápido...
function creaMedida(descripcion, tipo, valor){
return {
descripcion: descripcion,
[tipo]: valor
};
};
var circulo = creaMedida('circulo', 'superficie', 3);
var esfera = creaMedida('esfera', 'volumen', 5);
expect(circulo).toEqual({ descripcion: 'circulo', superficie: 3 });
expect(esfera).toEqual({ descripcion: 'esfera', volumen: 5 });
Clases
class Point {
constructor(x,y){
this.x_ = x;
this.y_ = y;
}
get y(){
return this.y_;
}
get x(){
return this.x_;
}
get theta() {
return Math.asin(this.y_ / this.radius)
}
get radius() {
return Math.sqrt(this.x_ * this.x_ + this.y_ * this.y_)
}
}
Definiendo la clase
Heredando
class Point3D extends Point{
constructor(x, y, z){
super(x, y);
this.z_ = z;
}
get radius() {
return Math.sqrt(super.radius * super.radius + this.z_ * this.z_);
}
proyectionXY(){
return new Point(this.x, this.y)
}
}
La singularidad...
class Singularity extends Point3D {
constructor(x, y, z, props = {mass:0, momento: 0}) {
super(x,y,z);
this.properties = props;
}
}
Parámetros en funciones
Default
Parámetros por defecto
function saludo(saludado = 'Mundo'){
return `¡Hola, ${saludado}!`;
}
expect(saludo() ).toEqual('¡Hola, Mundo!');
expect(saludo('Lucia')).toEqual('¡Hola, Lucia!')
Rest
class Tienda {
constructor(){
this.escaparate = {};
}
add(categoria, ...productos) {
this.escaparate[categoria] = this.escaparate[categoria] || [];
productos.forEach(producto => this.escaparate[categoria].push(producto));
}
}
var tienda = new Tienda();
tienda.add('fruta', 'manzanas', 'peras');
expect(tienda.escaparate.fruta).toEqual(['manzanas', 'peras']);
Spread
La inversa de Rest
var productosCarnicos = ['ternera', 'pollo', 'pavo'];
tienda.add('carne', ...productosCarnicos);
expect(tienda.escaparate.carne).toEqual(productosCarnicos);
O más habitual
expect(
Math.max(...[1,2,3,4,5])
).toEqual(5);
Módulos
// math.js
export var suma = (a,b) => a + b;
export var e = Math.E;
// app.js
import * as math from 'math';
expect( math.suma(1, 2) ).toBe(3);
expect( math.e ).toBe(Math.E);
// app.js
import {suma, e} from 'math';
expect( suma(1, 2) ).toBe(3);
expect( e ).toBe(Math.E);
import {Point, Point3D, Singularity} from '../puntos';
var punto = new Point()
export class Point {
//..
}
export class Point3D extends Point{
//...
}
export class Singularity extends Point3D {
//...
}
Nuevos objetos
Map
class Bote {
}
var bote = new Bote();
var cosas = new Map();
cosas.set(bote, 'mas cosas')
expect(cosas.get(bote)).toEqual('mas cosas');
Set
var cosas = new Set();
cosas.add('lapices').add('sacapuntas').add('lapices');
expect(cosas.size).toEqual(2);
expect(cosas.has('lapices')).toBeTrue()
Symbol
var norte = Symbol();
var este = Symbol();
var oeste = Symbol();
var sur = Symbol();
function mira(direccion) {
var direcciones = {};
direcciones[norte] = true;
direcciones[este] = true;
direcciones[oeste] = true;
direcciones[sur] = true;
return direcciones[direccion]
}
expect(mira(1)).toBeFalsy();
expect(mira(sur)).toBe(true);
WeakMap
WeakSet
¡Recursividad!
Proper Tail Calls
y no "Tail Call Optimization" o TCO:
las optimizaciones son transparentes y afectan a la eficiencia. Si la llamada no es "proper" no hay reutilización de callstack y el programa puede fallar, no es que sea más lento,
//AVISO: no funciona con arrays con 'undefined' en alguna posición
const reduce =
(fn, acc, [x, ...xs]) =>
x === undefined ? acc
: reduce(fn, fn(acc, x), xs);
//------------------------------------------------------//
const map =
(fn, xs) =>
reduce(
(acc, x) => [...acc, fn(x)],
[],
xs
);
var oneArr = [1];
var addOne = (arr) => oneArr.concat(arr);
var sum = (a,b) => a + b;
var thousandOnes = reduce(addOne, [], new Array(1000));
expect(reduce(sum, 0, thousandOnes)).toEqual(1000);
Prometiendo
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
timeout(10).then(() => done());
Promesas nativas
Simplificando el código asíncrono:
async & await
Una función "async" puede contener expresiones "await".
El operando de un "await" se trata como una Promesa, y cuando esta se completa la ejecución continúa.
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function asyncValue(value) {
await timeout(50);
return value;
}
(async function() {
var value = await asyncValue(42)
.catch(console.error.bind(console));
assert.equal(42, value);
done();
})();
ECMAScript Tools by @addyosmani
- Transpilers
- Grunt, Gulp & Broccoli tasks
- Module Loaders
- Boilerplate
- Code Generation
- Polyfills
- Editores
- Parsers
- y mas...
ECMAScript 6 compatibility table
ES6 con node.js
By Gonzalo Ruiz de Villa
ES6 con node.js
ECMAScript 6 con node.js. Cosas que puedes hacer con Traceur
- 5,103