Tutorial de Javascript y las mejoras desde ES6 (ES2015) hasta ES10 (ES2019) – Parte 6 (Features)

Las nuevas features de Javascript desde ES6 hasta ES10

En esta nueva entrada veremos todas (o casi todas) las nuevas características que esta ofreciendo Javascript.

Comencemos con la versión que mas cambios ha traido al Front End, ES6 (2015).

  • let
    • Podemos decir que se parece a la forma de usar var pero con alguna diferencia.
    • Esta nueva característica se define un alcance de bloque (block scope), de tal forma que la variable solo sera accesible desde el bloque donde se defina.
    • // Uso de VAR
      (() => {
          console.log('--- VAR ---');
          var numero = 100;
          console.log('OUT:', numero);
          {
              var numero = 1000;
              console.log('IN:', numero);
          }
          console.log('OUT 2:', numero);
      })();
      --- VAR ---
      OUT: 100
      IN: 1000
      OUT 2: 1000
      
      /* 
         Esto es debido al "hoisting", que es un comportamiento por defecto que tiene Javascript 
         moviendo todas las declaraciones al principio de la función o hasta el inicio del objeto window.
      */
      (() => {
          console.log('--- LET ---');
          let numero = 100;
          console.log('OUT:', numero);
          {
              let numero = 1000;
              console.log('IN:', numero);
          }
          console.log('OUT 2:', numero);
      })();
      --- LET ---
      OUT: 100
      IN: 1000
      OUT 2: 100

       

  • const
    • Como ocurre con let, también tiene un alcance de bloque (block scope).
    • Ahora podemos definir un valor constante, hasta cierto punto ya que no se permite reasignar un nuevo contenido.
    • Esto significa que:
      • Si el valor es de tipo primitivo no podríamos reasignar ya que en los tipos primitivos la asignación se hace por valor.
      • Si el valor es de tipo objeto entonces si podríamos reasignar ya que en los tipos objetos la asignación se hace por referencia.
    • const number = 10;
      const data = { number: 10 };
      data.number = 100;
      number = 100; // Script snippet %2321:4 Uncaught TypeError: Assignment to constant variable.
      
      
  • block scoped (alcance de bloque)
    • En versiones anteriores de Javascript se podía emular el alcance de un bloque envolviendo la función
    • En las nuevas versiones ya no es necesario
    • {
          function bool() {
              return true;
          }
          console.log(bool());
          // true
          {
              function bool() {
                  return false;
              }
              console.log(bool());
              // false
          }
          console.log(bool());
          // true
      }
  • arrow function (función flecha)
    • Algunas características
      • Tiene una sintaxis mas corta.
      • No tiene this propio.
      • No tiene arguments.
      • Siempre es anónima.
      • No se pueden usar como constructor.
      • Los paréntesis son opcionales si solo tiene un argumento, para el resto son obligatorios.
      • Si no tiene un bloque definido el return esta implícito, en caso contrario debería incluir un return de forma explícita.
      • Se pueden asignar parámetros por defecto.
      • const formaAbreviada = (a = 1, b = 2) => a + b;
        console.log(formaAbreviada());                       // 3
        console.log(formaAbreviada(5));                      // 7
        console.log(formaAbreviada(10, 10));                 // 20
        
        const contexto = msn => `${this}, ${msn}`;
        console.log(contexto('test'));                       // [object Window], test
        
        const bool = () => true;
        console.log(bool());                                 // true
        
        const multiple = (a = 1, b = 2, c = 3) => {
            let aa = a * 2;
            let bb = b * 3;
            let cc = c * 4;
            return aa + bb + cc;
        }
        console.log(multiple());                             // 20
        
        const objetoDefault = (nombre = '---', edad = '0') => ({
            nombre,
            edad,
        });
        console.log(objetoDefault());                        // { nombre: "---", edad: "0" }
        console.log(objetoDefault('aaaaa', 100));            // { nombre: "aaaaa", edad: 100 }
  • extend parameter handling (manejo extendido de parámetros)
    • Esta nueva característica incluye:
      • Default parameter value (valor de parámetros por defecto)
        • Permite añadir valores por defecto en las funciones
        • console.log('--- Default parameter value ---');
          const suma = (a = 10, b = 20) => a + b;
          // Valores por defecto
          console.log(suma());       // 30
          // Si incluye argumento sobreescribe el valor por defecto
          console.log(suma(2));      // 22
          // Si incluye argumento sobreescribe el valor por defecto
          console.log(suma(2, 5));   // 7
          
      • Rest parameter (resto de parametros)
        • Nos permite reunir los parámetros definidos en una función mediante el uso de ‘‘ seguidos del nombre de la matriz.
        • Muy importante incluir al final de la definición.
        • const fnSuma_1 = (a, b, ...resto) => {
              console.log('a', a);
              console.log('b', b);
              console.log('resto', resto);
              return a + b + resto.reduce((acc, curr) => acc + curr);
          }
          console.log(fnSuma_1(1,2,3,4,5,6,7,8));
          /*
          a 1
          b 2
          resto (6) [3, 4, 5, 6, 7, 8]
          36
          */
          
          const fnSuma_2 = (a, ...resto) => {
              console.log('a', a);
              console.log('resto', resto);
              return a + resto.reduce((acc, curr) => acc + curr);
          }
          console.log(fnSuma_2(1,2,3,4,5,6,7,8));
          /*
          a 1
          resto (7) [2, 3, 4, 5, 6, 7, 8]
          36
          */
      • Spread operator (operador de propagación)
        • Podemos usarlo en una función para agrupar múltiples argumentos (rest operator)
        • Podemos usarlo para generar una lista de valores a partir de una array o un string.
        • // ARRAY
          let arr1 = [1, 2, 3];               
          let arr2 = [4, 5, 6];               
          let arr3 = [...arr1, ...arr2];      
          console.log(arr1);                  // [1, 2, 3]
          console.log(arr2);                  // [4, 5, 6]
          console.log(arr3);                  // [1, 2, 3, 4, 5, 6]
          console.log(...arr1);               // 1 2 3
          console.log(...arr2);               // 4 5 6
          console.log(...arr3);               // 1 2 3 4 5 6
          
          // STRING
          let text1 = 'Prueba';
          console.log([...text1]);            // ["P", "r", "u", "e", "b", "a"]
  • Template literals (plantillas de texto)
    • Son literales que permiten usar expresiones incrustadas y para crearlas se usa la tilde invertida (`.).
    • Para incrustar las expresiones usaremos la siguiente sintaxis: ${expresión}
    • const datos = {
          a: 1,
          b: 2,
          c: true,
          d: 'saludo',
      };
      const msn = `El valor de a es ${datos.a}
                   El valor de b es ${datos.b}
                   ${datos.c ? datos.d : 'false'}`;
       
      console.log(msn);
      
      /*
      El valor de a es 1
                   El valor de b es 2
                   saludo
      */
  • Enhanced Object Properties (Propiedades de objeto mejoradas)
    • Esta nueva característica nos permite de una forma simple crear objetos usando notación literal, y como consecuencia una codificación mas clara.
      • Property shorthand (Propiedades «abreviadas»): Sintaxis más corta para la de definición de propiedades de un objeto.
      • let texto = 'texto';
        let estado = false;
        
        const ES6 = {
           texto,
           estado,
        };
        
      • Computed property names (Nombres de propiedades calculadas): Nombres calculados en la definición de las propiedades de un objeto.
      • const m = 'methods';
        
        const ES6_1 = {
            a: true,
            [m]: 100,
        };
        /*
        {
           a: true
           methods: 100
        }
        */
        
        const ES6_2 = {
            aa: true,
        };
        for (let cont = 0; cont < 10; cont++) {
            ES6_2[`${m}_${cont}`] = cont;
        }
        /*
          aa: true
          methods_0: 0
          methods_1: 1
          methods_2: 2
          methods_3: 3
          methods_4: 4
          methods_5: 5
          methods_6: 6
          methods_7: 7
          methods_8: 8
          methods_9: 9
        */
        
      • Method Properties (): Notación de métodos en la definición de las propiedades de un objetos.
      • const ES6_3 = {
            suna (a, b) {
                return a + b;
            },
            resta (a, b) {
                return a - b;
            }
        };
        
        ES6_3.suna(2,3);
        // 5
        ES6_3.resta(2,3);
        // -1
  • Destructuring Assignment (Asignación de Desestructuración)
    • Es una nueva característica que nos permite extraer de un array u objeto sus variables.
    • A continuación veremos las múltiples formas de extraer esa información
      • Array matching (Parear arrays): Desestructuración de arrays en variables durante la asignación.
      • const semana = ['Lunes', 'Martes', 'Miercoles', 'Jueves', 'Viernes', 'Sabado', 'Domingo'];
        const [l, m, x, j, v, s, d] = semana;
        
        console.log(semana);
        // (7) ["Lunes", "Martes", "Miercoles", "Jueves", "Viernes", "Sabado", "Domingo"]
        
        console.log(l, m, x, j, v, s, d);
        // Lunes Martes Miercoles Jueves Viernes Sabado Domingo
      • Object Matching (Parear objetos): Desestructuración de objetos en variables durante la asignación.
      • const semana = {
            l: 'Lunes', 
            m: 'Martes', 
            x: 'Miercoles', 
            j: 'Jueves', 
            v: 'Viernes', 
            s: 'Sabado', 
            d: 'Domingo',
        };
        const {l, m, x, j, v, s, d} = semana;
        
        console.log(semana);
        /*
        {
           l: "Lunes"
           m: "Martes"
           x: "Miercoles"
           j: "Jueves"
           v: "Viernes"
           s: "Sabado"
           d: "Domingo"
        */
        
        console.log(l, m, x, j, v, s, d);
        // Lunes Martes Miercoles Jueves Viernes Sabado Domingo
      • const datos = {
            nombre: 'nombre',
            edad: 20,
            direccion: {
                nombre: 'Calle Pino',
                numero: 5,
                planta: 3,
                puerta: 'A',
                poblacion: 'Población',
                Provincia: 'Provincia',
                cp: '12122',
            },
            estudios: {
                reglados: {
                    nombre: 'X',
                    duracion: 4,
                },
            },
        };
        
        const {
            direccion: {
                nombre,
                numero,
                poblacion,
                provincia,
            },
            estudios: {
                reglados,
            },
        } = datos;
        
        console.log(nombre, numero, poblacion, provincia, reglados);
        // Calle Pino 5 Población undefined {nombre: "X", duracion: 4}
        
      • Default values in object and array (Valores defecto en objetos y arrays): Añadir valores defecto a la desestructuración de arrays y objetos
      • const infoObject = {
            a: 1,
            b: false,
        };
        const infoObjectTemp = {
            at: 1,
        };
        const infoArray = [1, 2, 3];
        
        const {a, b = true } = infoObject;
        console.log(a,b);
        // 1 false
        
        const {at, bt = true } = infoObjectTemp;
        console.log(at, bt);
        // 1 true
        
        const [ar = 0, br = 0, cr = 0, dr = 0] = infoArray;
        console.log(ar, br, cr, dr);
        // 1 2 3 0
      • Parameter Context Matching (Parear los parámetro del contexto): Añadir el pareo y valores por defecto en las llamadas a las funciones de objetos y arrays
      • const sumaArr = ([param1, param2]) => param1 + param2;
        console.log(sumaArr([1, 9]));
        // 10
        
        const objTest_1 = ({param1 = 'default', param2 = 'default'}) => ({ param1, param2});
        console.log(objTest_1({}));
        // {param1: "default", param2: "default"}
        
        console.log(objTest_1({param1: 1}));
        // {param1: 1, param2: "default"}
        
        console.log(objTest_1({param1: 1, param2: 9}));
        // {param1: 1, param2: 9}
        
        const objAlias_1 = ({param1: p1 = 'default', param2: p2 = 'default'}) => ({ p1, p2});
        console.log(objAlias_1({}));
        // {p1: "default", p2: "default"}
        
        console.log(objAlias_1({param1: 1}));
        // {p1: 1, p2: "default"}
        
        console.log(objAlias_1({param1: 1, param2: 9}));
        // {p1: 1, p2: 9}
  • Modules (Módulos)
    • Es una forma de compartir y encapsular nuestro código.
      • Export / import: Podemos exportar e importar módulos
      • // string.js
        export upper = (msn = '') => msn.toUpperCase();
        export lower = (msn = '') => msn.toLowerCase();
        
        // script.js
        import * as str from 'string.js';
        
        str.upper('Hola');
        // HOLA
        
        str.lower('Hola');
        // hola
        
        // newScript.js
        import {upper, lower} from 'string.js';
        
        str.upper('Hola');
        // HOLA
        
        str.lower('Hola');
        // hola
      • Default: Valor por defecto
      • // string.js
        export default (msn = '') => msn.toUpperCase();
        
        // script.js
        import upper from 'string.js';
        upper('Hola');
        // HOLA
  • Classes (Clases)
    • Con esta nueva característica podemos definir clases usando el estilo de programación orienta a objetos clásica.
      • Class definition (Definición de clases): Definición de clases.
      • class Persona {
            constructor(name, age) {
                this.name = name;
                this.age = age;
            }
        }
        
        const p1 = new Persona('a', 1);
        console.log(p1);
        // Persona {name: "a", age: 1}
        
        const p2 = new Persona('b', 2);
        console.log(p2);
        // Persona {name: "b", age: 2}
        
      • Inheritance (Herencia): Usando herencia
      • class Persona {
            constructor(name, age) {
                this.name = name;
                this.age = age;
            }
        }
        class Profession extends Persona {
            constructor(name, age, jobPosition, salary) {
                super(name, age);
                this.jobPosition = jobPosition;
                this.salary = salary;
            }
        }
        
        const p1 = new Profession('a', 1, 'developer', 40000);
        console.log(p1);
        // Profession {name: "a", age: 1, jobPosition: "developer", salary: 40000}
      • Static class members (Miembros de clase estática): Acceso sencillo a los miembros estáticos de una clase.
      • class Persona {
            constructor(name, age) {
                this.name = name;
                this.age = age;
            }
            saludo() {
                return `Hola ${this.name} y tienes ${this.age} años`;
            }
            static welcome() {
                return 'Bienvenidos, método estático';
            }
        }
        
        console.log(Persona.welcome());
        // "Bienvenidos, método estático"
        
        const p1 = new Persona('a', 1);
        console.log(p1.saludo());
        // "Hola a y tienes 1 años"
      • Getters / Setters: Getters y setters dentro de las clases.
      • class Persona {
            constructor(name, age) {
                this._name = name;
                this._age = age;
            }
            saludo() {
                return `Hola ${this._name} y tienes ${this._age} años`;
            }
            static welcome() {
                return 'Bienvenidos, método estático';
            }
            get name() {
                return this._name;
            }
            set name(value) {
                this._name = value;
            }
            get age() {
                return this._age;
            }
            set age(value) {
                this._age = value;
            }
        }
        
        console.log(Persona.welcome());
        // "Bienvenidos, método estático"
        
        const p1 = new Persona('a', 1);
        console.log(p1);
        // Persona {_name: "a", _age: 1}
        
        console.log(p1.name);
        // "a"
        
        console.log(p1.age);
        // 1
        
        p1.name = 'aaaaaa'
        console.log(p1);
        // Persona {_name: "aaaaaa", _age: 1}
        
        p1.age = 1000
        console.log(p1);
        // Persona {_name: "aaaaaa", _age: 1000}
  • Symbol
    • Es un tipo de dato primitivo, único e inmutable.
      • Se le puede pasar un parámetro pero es opcional.
      • Aunque tengan la misma descripción, siguen siendo únicos.
      • const COL_NAME_USERS = Symbol('name');
        const COL_NAME_USERS_2 = Symbol('name');
        const COL_SURNAME_USERS = Symbol('surname');
        const COL_SURNAME_USERS_2 = Symbol('surname');
        
        console.log(COL_NAME_USERS === COL_NAME_USERS_2);
        // false
        console.log(COL_SURNAME_USERS === COL_SURNAME_USERS_2);
        // false
  • Iterators (Iteradores)
    • Es un objeto que nos permite recorrer una colección mediante el método next(), para devolvernos al final un objeto con dos propiedades.
      • value: Corresponde al siguiente valor de la iteración
      • done: Cuando esta propiedad esta a true entonces significa que termino la iteración, mientras tanto nos devolverá false y la propiedad value.
      • const iterar = arr => {
            let index = 0;
            return {
                next: () => {
                    return index < arr.length ? { value: arr[index++], done: false} : {done:true}
                }
            }
        }
        
        const iterador = iterar(['a','b','c',1,2,3]);
        console.log(iterador.next());
        console.log('-------');
        console.log('Primer valor del array', iterador.next().value);
        console.log('Podemos hacer lo que necesitemos ahora')
        for (let cont = 0; cont < 10; cont++) {
            console.log(cont);
        }
        console.log('Siguiente valor del array', iterador.next().value);
        console.log('Otro', iterador.next().value);
        console.log('Otro', iterador.next().value);
        console.log('Otro', iterador.next().value);
        console.log('Otro', iterador.next().value);
        console.log('Otro', iterador.next().value);
        console.log('Otro', iterador.next().value);
        console.log('Otro', iterador.next().value);
        console.log('Otro', iterador.next().value);
        console.log('Otro', iterador.next().value);
        console.log('Otro', iterador.next().value);
        
        /*
        {value: "a", done: false}
        -------
        Segundo valor del array b
        Podemos hacer lo que necesitemos ahora
        0
        1
        2
        3
        4
        5
        6
        7
        8
        9
        Siguiente valor del array c
        Otro 1
        Otro 2
        Otro 3
        Otro undefined
        Otro undefined
        Otro undefined
        Otro undefined
        Otro undefined
        Otro undefined
        Otro undefined
        */
  • Generators (Generadores)
    • Se puede definir como un iterador especial donde podemos parar y reanudar el flujo de control.
      • function* generate1to10() {
            yield 1;
            yield 2;
            yield 3;
            yield 4;
            yield 5;
            yield 6;
            yield 7;
            yield 8;
            yield 9;
            yield 10;
        }
        const generate = generate1to10();
        
        generate.next()
        // {value: 1, done: false}
        generate.next()
        // {value: 2, done: false}
        generate.next()
        // {value: 3, done: false}
        generate.next()
        // {value: 4, done: false}
        generate.next()
        // {value: 5, done: false}
        generate.next()
        // {value: 6, done: false}
        generate.next()
        // {value: 7, done: false}
        generate.next()
        // {value: 8, done: false}
        generate.next()
        // {value: 9, done: false}
        generate.next()
        // {value: 10, done: false}
        generate.next()
        // {value: undefined, done: true}
      • // Simulate range in Python
        function* range(start = 0, stop = 1, step = 1) {
            let init = start;
            while(init < stop) {
                yield init+=step;     
            }
        }
        
        for (i of range(0, 20, 4)) {
            console.log(i);
        }
        
        const r = range(0, 20, 4);
        console.log(r.next());
        console.log(r.next());
        console.log(r.next());
        console.log(r.next());
        console.log(r.next());
        console.log(r.next());
        
        /*
        4
        8
        12
        16
        20
        {value: 4, done: false}
        {value: 8, done: false}
        {value: 12, done: false}
        {value: 16, done: false}
        {value: 20, done: false}
        {value: undefined, done: true}
        */
  • Map
    • El objeto Map nos permite almacenar pares de clave/valor de cualquier tipo, tanto objetos como primitivos.
      • El objeto Map tiene múltiples métodos y propiedades que nos permiten manipular su contenido
      • const mapTest = new Map();
        const obj = {
            nombre: 'aaa',
            edad: 1,
            estado: false,
        };
        
        mapTest.set('obj', obj);
        mapTest.set('nombre', 'bbb');
        console.log(mapTest);
        // Map(2) {"obj" => {…}, "nombre" => "bbb"}
        
        console.log(mapTest.size)
        // 2
        
        console.log(mapTest.get('nombre'));
        // bbb
        
        console.log(mapTest.get('obj'));
        // {nombre: "aaa", edad: 1, estado: false}
        
        mapTest.delete('obj');
        console.log(mapTest);
        // {"nombre" => "bbb"}
  • Set
    • El objeto Set consta de una colección de valores y solo puede estar una vez el valor de esa colección.
      • Como ocurre con el objeto Map, este nos ofrece también múltiples métodos y propiedades para su manipulación.
      • const setTest = new Set();
        
        setTest.add('a');
        setTest.add(true);
        setTest.add({a:1, status: true});
        
        console.log(setTest);
        // {"a", true, {…}}
        
        console.log(setTest.has(true));
        // true
        
        console.log(setTest.has('a'));
        // true
        
        console.log(setTest.has('b'));
        // false
        
        setTest.delete('a');
        console.log(setTest);
        // {true, {…}}[[Entries]]0: true1: Object2: "a"size: (...)__proto__: Set
        
        setTest.add('a');
        setTest.add('a');
        setTest.add('a');
        setTest.add('a');
        setTest.add('a');
        console.log(setTest);
        // {true, {…}, "a"}
  • Object.assign
    • Con el método Object.assign() podemos copiar los valores de todas las propiedades enumerables de uno o múltiples objetos a un destino. Esta operación nos devuelve el objeto destino.
      • const a = { a: 1 };
        const b = { b: 1 };
        const c = { c: 1 };
        const d = { d: 1 };
        
        const result_1 = { destino: true };
        console.log(result_1);
        // {destino: true}
        
        Object.assign(result_1, a, b, c, d);
        console.log(result_1);
        // {destino: true, a: 1, b: 1, c: 1, d: 1}
        
        const result_2 = Object.assign(result_1, a, b, c, d);
        console.log(result_2);
        // {destino: true, a: 1, b: 1, c: 1, d: 1}
        
        const result_3 = Object.assign({}, result_1, a, b, c, d);
        console.log(result_3);
        // {destino: true, a: 1, b: 1, c: 1, d: 1}
  • Array
    • Array.find(): Nos devuelve el valor de la primera coincidencia que cumpla el criterio.
    • Array.findIndex(): Nos devuelve el indice de la primera coincidencia que cumpla el criterio.
      • [1,2,3,4,5,6,7,8,9].find(v => v > 5)
        // 6
        [1,2,3,4,5,6,7,8,9].findIndex(v => v > 5)
        // 5
        
    • Array.from(): Crea un array a partir de un objeto iterable
      • const arr = Array.from('Prueba de texto');
        console.log(arr);
        // ["P", "r", "u", "e", "b", "a", " ", "d", "e", " ", "t", "e", "x", "t", "o"]
        
      • const cesarCypher = 'Mensaje usando cifrado Cesar';
        const offset = 4;
        const cypher = Array.from(cesarCypher, letter => String.fromCharCode(letter.charCodeAt(0) + offset));
        console.log(cypher);
        // ["Q", "i", "r", "w", "e", "n", "i", "$", "y", "w", "e", "r", "h", "s", "$", "g", "m", "j", "v", "e", "h", "s", "$", "G", "i", "w", "e", "v"]
        console.log(Array.from(cesarCypher));
        // ["M", "e", "n", "s", "a", "j", "e", " ", "u", "s", "a", "n", "d", "o", " ", "c", "i", "f", "r", "a", "d", "o", " ", "C", "e", "s", "a", "r"]
      • const obj = {
            length: 4,
            0: 'a',
            1: 'b',
            2: 1,
            3: false,
        };
        console.log(Array.from(obj));
        // ["a", "b", 1, false]
    • Array.fill(): Cambia todos los elementos de un array por un valor estático, devolviendo un array modificado.
      • const arr = [1,2,3,4,5,6,7,8,9,10];
        console.log(arr.fill(0));
        // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        console.log(arr.fill('a', 2));
        // [0, 0, "a", "a", "a", "a", "a", "a", "a", "a"]
        console.log(arr.fill(true, 3, 5));
        // [0, 0, "a", true, true, "a", "a", "a", "a", "a"]

         

  • String.repeat(), String.startsWith(), String.endsWith(), String.includes()
    • String.repeat(): Devuelve una cadena nueva que contiene el número especificado de copias de la cadena original (concatenado).
    • String.startsWith(): Devuelve un valor boleano que nos permite saber si una cadena comienza o no con otra cadena (Distingue entre mayúsculas y minúsculas).
    • String.endsWith(): Devuelve un valor boleano que nos permite saber si una cadena termina o no con otra cadena (Distingue entre mayúsculas y minúsculas).
    • String.includes(): Devuelve un valor boleano que nos permite determinar si una cadena de texto se encuentra incluida dentro de la otra.
      • const saludo = "Hola";
        console.log(saludo.repeat(10));
        // HolaHolaHolaHolaHolaHolaHolaHolaHolaHola
        
        const texto = "En un lugar de la Mancha";
        
        console.log(texto.startsWith('Mancha', 19)); // false
        console.log(texto.startsWith('En un'));      // true
        console.log(texto.startsWith('En '));        // true
        
        console.log(texto.endsWith('Mancha'));       // true
        console.log(texto.endsWith('En un', 10));    // false
        console.log(texto.endsWith('En '));          // false
        
        console.log(texto.includes('Mancha'));       // true
        console.log(texto.includes('Avión'));        // false
        console.log(texto.includes('un', 3));        // true
        
  • Promises (Promesas)
    • Es un objeto que representa la resolución o el fallo eventual de una operación asíncrona.
      • const asyncTest = () => {
            return new Promise((resolve, reject) => {
               setTimeout(() => {
                   const rnd = Math.floor(Math.random() * 100);
                   if (rnd < 50) {
                       resolve('OK');
                   } else {
                       reject('FAIL');
                   }
               }, 1000) 
            });
        };
        
        asyncTest().then(data => console.log(data)).catch(err => console.log(err));
        // Promise {<pending>} FAIL
        
        asyncTest().then(data => console.log(data)).catch(err => console.log(err));
        // Promise {<pending>} OK
        
        asyncTest().then(data => console.log(data)).catch(err => console.log(err));
        // Promise {<pending>} FAIL
        
        asyncTest().then(data => console.log(data)).catch(err => console.log(err));
        // Promise {<pending>} OK
      • const asyncTest = () => {
            return new Promise((resolve, reject) => {
               setTimeout(() => {
                   const rnd = Math.floor(Math.random() * 100);
                   if (rnd < 50) {
                       resolve('OK');
                   } else {
                       reject('FAIL');
                   }
               }, 1000) 
            });
        };
        
        Promise.all([asyncTest(),asyncTest()]).then(data => console.log(data)).catch(err => console.log(err))
        // Promise {<pending>} ["OK", "OK"]
        
        Promise.all([asyncTest(),asyncTest()]).then(data => console.log(data)).catch(err => console.log(err))
        // Promise {<pending>} FAIL
        
        Promise.all([asyncTest(),asyncTest()]).then(data => console.log(data)).catch(err => console.log(err))
        // Promise {<pending>} ["OK", "OK"]
      • // API de Star Wars
        
        const baseURL = 'https://swapi.co/api/people/';
        const stack = [];
        for (let cont = 1; cont < 10; cont++) {
            let promesa = fetch(`${baseURL}${cont}/`);
            stack.push(promesa);
        }
        Promise.all(stack)
        .then(response => {
           response.forEach(async res => {
               let result = await res.json();
               console.log(result);
           })
        })
        .catch(err => {
            console.log(err);
        });
        
        /* 
        {name: "Luke Skywalker", height: "172", mass: "77", hair_color: "blond", skin_color: "fair", …}
        {name: "R2-D2", height: "96", mass: "32", hair_color: "n/a", skin_color: "white, blue", …}
        {name: "Darth Vader", height: "202", mass: "136", hair_color: "none", skin_color: "white", …}
        {name: "Leia Organa", height: "150", mass: "49", hair_color: "brown", skin_color: "light", …}
        {name: "Owen Lars", height: "178", mass: "120", hair_color: "brown, grey", skin_color: "light", …}
        {name: "Beru Whitesun lars", height: "165", mass: "75", hair_color: "brown", skin_color: "light", …}
        {name: "R5-D4", height: "97", mass: "32", hair_color: "n/a", skin_color: "white, red", …}
        {name: "Biggs Darklighter", height: "183", mass: "84", hair_color: "black", skin_color: "light", …}
        {name: "C-3PO", height: "167", mass: "75", hair_color: "n/a", skin_color: "gold", …}
        */
  • Proxy, Reflect (Metaprogramación)
    • Proxy
      • Es un objeto que nos permite capturar operaciones de lectura y escritura sobre las propiedades de un objeto.
      • Debemos usar la palabra reservada new ya que es un Constructor.
      • const datos = {
            nombre: '',
            edad: 0,
            estado: false,
        };
        let proxyExample = new Proxy(datos, {
            get: (target, propertyKey, receiver) => {
                if (!target.nombre || target.edad === 0) {
                    throw new Error('Es necesario completar el nombre y/o la edad')
                }
                return target[propertyKey];
            },
            set: (target, prop, value, receiver) => {
                console.log(prop)
                if (prop === 'edad') {
                    if (isNaN(value)) {
                        throw new Error('La edad tiene que ser un valor numérico');
                    }    
                }
                
                // Se puede usar ambas opciones, pero la documentación oficial recomienda hacer uso de del objecto Reflect
                // target[prop] = value;
                Reflect(target, prop, value);
                return true;
            },
        });
        
        
        proxyExample
        // Proxy {nombre: "", edad: 0, estado: false}
        
        proxyExample.nombre
        // Proxy:9 Uncaught Error: Es necesario completar el nombre y/o la edad at Object.get (Proxy:9) at <anonymous>:1:14 get @ Proxy:9
        
        proxyExample.edad
        // Proxy:9 Uncaught Error: Es necesario completar el nombre y/o la edad at Object.get (Proxy:9) at <anonymous>:1:14 get @ Proxy:9
        
        proxyExample.nombre = 'aaa'
        // Proxy:14 nombre "aaa"
        
        proxyExample.edad = 'aaa'
        /*
        edad
        Proxy:17 Uncaught Error: La edad tiene que ser un valor numérico at Object.set (Proxy:17)at <anonymous>:1:19 set @ Proxy:17
        */
        
        proxyExample.edad = 100
        // edad 100
    • Reflect
      • Reflect  es un objecto que nos proporciona métodos para interceptar operaciones de Javascript y con los mismos que el objeto Proxy.
      • A diferencia de otros objetos globales, este no es un constructor, por lo tanto no se puede instanciar usando new invocarse como una función.
      • Todas sus propiedades y métodos son estáticos.
      • const datos = {};
        Reflect.set(datos, 'nombre', 'Texto');
        console.log(datos);
        // {nombre: "Texto"}
        
        datos.apellidos = 'aaa';
        console.log(datos);
        // {nombre: "Texto", apellidos: "aaa"}
        
        const ok = Reflect.set(datos, 'edad', 10)
        console.log(ok);
        // true
        
        console.log(Reflect.get(datos, 'edad'));
        // 10
        
      • function method(msn) { console.log(`Mensaje: ${msn}`, this); }
        
        Reflect.apply(method, undefined, ['Hola 1']);
        // Mensaje: Hola 1 Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}
        
        Reflect.apply(method, this, ['Hola 2']);
        // Mensaje: Hola 2 Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}
        
        Reflect.apply(method, {a: 1}, ['Hola 3']);
        // Mensaje: Hola 3 {a: 1}
  • Internationalization
    • Intl
      • Este objeto global es el espacio de nombres para el API de Internacionalización.
      • Nos ofrece comparación de cadenas, formato de números, fechas y tiempos.
      • // Números 
        const EN = new Intl.NumberFormat("en-US")
        const DE = new Intl.NumberFormat("de-DE")
        const ES = new Intl.NumberFormat("es-ES")
        
        console.log(EN.format(1234567.89));  // 1,234,567.89
        console.log(DE.format(1234567.89));  // 1.234.567,89
        console.log(ES.format(1234567.89));  // 1.234.567,89
        
        // Moneda
        const USD = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" })
        const GBP = new Intl.NumberFormat("en-GB", { style: "currency", currency: "GBP" })
        const EUR = new Intl.NumberFormat("es-ES", { style: "currency", currency: "EUR" })
        
        console.log(USD.format(100200300.40));  // $100,200,300.40
        console.log(GBP.format(100200300.40));  // £100,200,300.40
        console.log(EUR.format(100200300.40));  // 100.200.300,40 €
        
        // Fechas
        const EN = new Intl.DateTimeFormat("en-US");
        const DE = new Intl.DateTimeFormat("de-DE");
        const ES = new Intl.DateTimeFormat("es-ES");
        console.log(EN.format(new Date("2020-12-31")));  // 12/31/2020
        console.log(DE.format(new Date("2020-12-31")));  // 31.12.2020
        console.log(ES.format(new Date("2020-12-31")));  // 31/12/2020
        

Continuamos con la versión ES7 (2016).

  • Array.includes()
    • Comprueba si existe un determinado valor dentro del array, devolviendo un valor boleano a true si existe o false si no existe.
      • console.log([1, 2, 3].includes(4))
        // false
        console.log([1, 2, 3].includes(2))
        // true
        console.log(['a', 'b', 'c'].includes('z'))
        // false
        console.log(['a', 'b', 'c'].includes('c'))
        // true
  • Operador exponencial (**)
    • Eleva el primer operando a la potencia del segundo operando.
      • const operA = 2;
        const operB = 3;
        const operC = operA ** operB;
        console.log(operC);
        // 8

Continuamos con la versión ES8 (2017).

  • String.padStart()
    • Rellena la cadena actual con los valores indicados devolviendo una cadena resultante hasta alcanzar la longitud dada.
    • El relleno se realiza desde el inicio de la cadena.
      • console.log('texto'.padStart(10));
        //      texto
        console.log('texto'.padStart(10, '1'));
        // 11111texto
        
  • String.padEnd()
    • Rellena la cadena actual con los valores indicados devolviendo una cadena resultante hasta alcanzar la longitud dada.
    • El relleno se realiza desde el final de la cadena.
      console.log('texto'.padEnd(10));
      // 'texto     '
      console.log('texto'.padEnd(10, '1'));
      // texto11111
  • Object.values()
    • Devuelve un array que contiene los valores de las propiedades enumerables de un objeto.
      • const obj = {
            nombre: 'a',
            apellidos: 'b',
            edad: 1,
            estado: true,
        };
        const arr = [1,2,3,4,5];
        console.log(Object.keys(obj));
        // ["nombre", "apellidos", "edad", "estado"]
        
        console.log(Object.keys(arr));
        // ['0', '1', '2', '3', '4']
  • Object.entries()
    • Devuelve un array que contiene los valores de las propiedades enumerables de un objeto, como un array [clave, valor].
      • const obj = {
            nombre: 'a',
            apellidos: 'b',
            edad: 1,
            estado: true,
        };
        const arr = [1, 2, 3, 4, 5];
        
        console.log(Object.entries(obj));
        /*
           0: (2) ["nombre", "a"]
           1: (2) ["apellidos", "b"]
           2: (2) ["edad", 1]
           3: (2) ["estado", true]
        */
        console.log(Object.entries(arr));
        /*
           0: (2) ["0", 1]
           1: (2) ["1", 2]
           2: (2) ["2", 3]
           3: (2) ["3", 4]
           4: (2) ["4", 5]
        */
  • Object.getOwnPropertyDescriptors()
    • Devuelve todos los descriptores de las propiedades propias de un objeto.
      • const obj = {
            nombre: 'a',
            apellidos: 'b',
            edad: 1,
            estado: true,
        };
        console.log(Object.getOwnPropertyDescriptors(obj));
        /*
        nombre:
           value: "a"
           writable: true
           enumerable: true
           configurable: true
           __proto__: Object
        apellidos:
           value: "b"
           writable: true
           enumerable: true
           configurable: true
           __proto__: Object
        edad:
           value: 1
           writable: true
           enumerable: true
           configurable: true
           __proto__: Object
        estado:
           value: true
           writable: true
           enumerable: true
           configurable: true
           __proto__: Object
        */
  • Async / Await
    • Es una forma de simplificar el uso síncrono de promesas, sin  tener que hacer uso de los bloque then catch.
      • const test1 = async () => {
            return true;
        };
        console.log(test1());
        // Promise {<resolved>: true}
        
        
        // ------------------------------
        const api = () => {
            return new Promise((res, rej) => {
                setTimeout(() => {
                    res('OK');
                }, 2000);
            });
        };
        
        const test = async () => await api();
        
        test().then(data => console.log(data));
        // OK
        
        
        // -------------------------------
        const t = async msn => `Mensaje..... ${msn}`;
        console.log(t('a'));
        // Promise {<resolved>: "Mensaje..... a"}

Continuamos con la versión ES9 (2018).

  • Asynchronous Iteration (for await…of)
    • Nos permite crear un bucle iterando sobre objetos iterables asincrónicos y sincrónicos.
    • // Generador asincrono
      async function* gen() {
          let cont = 0;
          while (cont < 10) {
              yield cont++;
          }
      }
      (async () => {
          for await (let item of gen()) {
              console.log(item);
          }    
      })();
      
      // Iterando promesas 
      const createPromise = () => {
          return new Promise((res, rej) => {
              setTimeout(() => {
                  res(true);
              }, 500);
          })
      }
      async function* gen() {
          debugger;
          let cont = 0;
          while (cont < 10) {
              cont++;
              yield await createPromise();
          }
      }
      (async () => {
          for await (let item of gen()) {
              console.log('Promise', item);
          }    
      })();
      
      // Iterando array de promesas
      
      const createPromise = () => {
          return new Prmise((res, rej) => {
              setTimeout(() => {
                  res(true);
              }, 500);
          });
      };
      
      (async () => {
          const arr = [];
          for (let cont = 0; con < 10; cont++) {
              arr.push(createPromise());
          }
          for await (let item of arr) {
              console.log('Promise', item);
          }
      })();
  • Promise.finally()
    • Este método devuelve una Promise cuando se resuelve allá tenido éxito o no.
    • Con este comportamiento podemos tener código que siempre se ejecuta.
    • const createPromise = () => {
          return new Promise((res, rej) => {
              setTimeout(() => {
                  const v = Math.floor(Math.random() * 100);
                  v <=50 ? res(true) : rej(false);
              }, 500);
          })
      }
      
      createPromise().then(r => console.log('OK', r)).catch(e => console.log('FAIL', e)).finally(() => console.log('ALWAYS'));
      /*
      Promise {<pending>}
      FAIL false
      ALWAYS
      */
      
      createPromise().then(r => console.log('OK', r)).catch(e => console.log('FAIL', e)).finally(() => console.log('ALWAYS'));
      /*
      Promise {<pending>}
      OK true
      ALWAYS
      */
      
      createPromise().then(r => console.log('OK', r)).catch(e => console.log('FAIL', e)).finally(() => console.log('ALWAYS'));
      /*
      Promise {<pending>}
      FAIL false
      ALWAYS
      */

Continuamos con la versión ES10 (2019).

  • Array.flat(depth)
    • Crea un nuevo array con todos los elementos concatenados recursivamente hasta la profundidad especificada.
    • const arr = [1, 2, 3, [4, 5, 6, [7, 8, 9, [10], 11], 12, 13], 14, 15];
      
      console.log(arr.flat());
      // (11) [1, 2, 3, 4, 5, 6, Array(5), 12, 13, 14, 15]
      
      console.log(arr.flat(1));
      // (11) [1, 2, 3, 4, 5, 6, Array(5), 12, 13, 14, 15]
      
      console.log(arr.flat(2));
      // (15) [1, 2, 3, 4, 5, 6, 7, 8, 9, Array(1), 11, 12, 13, 14, 15]
      
      console.log(arr.flat(3));
      // (15) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
      
      console.log(arr.flat(4));
      // (15) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
  • Array.flatMap()
    • Es similar a Array.flat(), salvo que este método primero mapea cada elemento y luego aplana el resultado en un nuevo array (profundidad de 1).
    • const arr = [1, 2, 3, [4, 5, 6, [7, 8, 9, [10], 11], 12, 13], 14, 15];
      
      console.log(arr.flatMap(item => item - 1))
      // (6) [0, 1, 2, NaN, 13, 14]
      
      console.log(arr.flatMap(item => item * 10))
      // (6) [10, 20, 30, NaN, 140, 150]
      
      console.log(arr.flatMap(item => item > 2 ? item * 10 : false))
      // (6) [false, false, 30, false, 140, 150]
      
      console.log(arr.flatMap(item => item > 2 ? item * 10 : null))
      // (6) [null, null, 30, null, 140, 150]
      
      console.log(arr.flatMap(item => item > 2 ? item * 10 : null).filter(item => item))
      // (3) [30, 140, 150]
      
  • Object.fromEntries()
    • Este método nos transforma una lista de pares [clave, valor] en un objeto.
    • const obj = {
          a: 1,
          b: 2,
          c: 3
      };
      
      console.log(Object.fromEntries(Object.entries(obj)));
      // {length: 3, a: 1, b: 2, c: 3}
      
      console.log(Object.fromEntries(new Map([['nombre', 'aaaa'], ['apellidos', 'bbbbb']])));
      // {nombre: "aaaa", apellidos: "bbbbb"}
      
      console.log(Object.fromEntries([['nombre', 'aaaa'], ['apellidos', 'bbbbb']]));
      // {nombre: "aaaa", apellidos: "bbbbb"}
  • String.trimStart(), String.trimEnd()
    • trimStart(): Elimina los espacios en blanco al principio de una cadena de texto.
    • trimEndt(): Elimina los espacios en blanco al final de una cadena de texto.
    • let text = '    Hola mundo!!!      ';
      
      console.log(text.trimEnd())
      // '    Hola mundo!!!'
      
      console.log(text.trimStart())
      // 'Hola mundo!!!    '
      
  • Optional Catch Binding
    • Nos permite usar en un bloque try/catch el bloque catch sin parámetro
    • try {
          throw new Error('Error')
      } catch {
          console.log('Error en el código');
      }
      
      // Error en el código
  • String.matchAll()
    • Nos devuelve un iterador con todos los resultados de ocurrencia en una cadena de texto contra una expresión regular.
    • const expresion = 't';
      const texto = 'Todos los dias tomo una tostada';
      const arr = [...texto.matchAll(expresion)];
      console.log(arr);
      /*
      0: ["t", index: 15, input: "Todos los dias tomo una tostada", groups: undefined]
      1: ["t", index: 24, input: "Todos los dias tomo una tostada", groups: undefined]
      2: ["t", index: 27, input: "Todos los dias tomo una tostada", groups: undefined]
      */
      
      
  • Dynamic import
    • La expresión import carga un módulo y este devuelve una promesa que se resuelve en un objeto de módulo que contiene todas sus exportaciones.
    • En la nueva revisión podemos importar dinámicamente esos módulos.
    • // externo.js
      export const suma = (a, b) => a + b;
      export const resta = (a, b) => a - b;
      
      // index.js
      const { suma, resta } = await import('./externo.js');
  • globalThis
    • Es una nueva forma con la que podemos acceder al this global
    • console.log(window);
      // Window {parent: Window, opener: null, top: Window, length: 2, frames: Window, …}
      
      console.log(globalThis);
      // Window {parent: Window, opener: null, top: Window, length: 2, frames: Window, …}

Bien, con esto terminamos esta guía donde he intentado estructurar todo (o casi todo) el contenido de Javascript ademas de todas las features de cada una de las revisión de ECMAScript.