Durante los últimos años han habido muchas actualizaciones en el lenguaje de JavaScript. Siendo estas muy útiles para los que buscan mejorar como programador.

Mantenerse al día con los nuevos desarrollos del lenguaje es realmente importante. Puede ayudarte a conseguir un trabajo mejor remunerado, mantenerte actualizado con las ultimas tendencias, mejorar la calidad de tu código, y sobresalir en tu trabajo actual.

Y sin lugar a dudas necesitas conocer las funcionalides más recientes si estás tratando de aprender una librería de JavaScript como React o un framework como Angular o Vue.

Recientemente, el lenguaje de JavaScript ha recibido muchas adiciones útiles, como encadenamiento opcional, promesas, async/await, desestructuración, y más.

Hoy veremos algunos de estos conceptos, los cuales todo desarrollador de JavaScript debería conocer.

¡Empecemos!

Let y const en JavaScript

Antes de ES6, JavaScript usaba la palabra clave var la cual solo cuenta con un ámbito global o funcional. El ámbito de bloque no existía.

Con la adición de let y const JavaScript incorporó este último.

Como usar let en JavaScript

Cuando declaramos una variable usando la palabra clave let, podemos asignar más tarde un nuevo valor a dicha variable, pero no podemos re-declararla con el mismo nombre.

// Código ES5
var valor = 10;
console.log(valor); // 10

var valor = "hola";
console.log(valor); // hola

var valor = 30;
console.log(valor); // 30

Como puedes ver arriba, hemos vuelto a declarar la variable valor usando la palabra clave var repetidas veces.

Antes de ES6, pudimos volver a declarar una variable que ya había sido declarada antes si esta no se usaba de manera significativa y en cambio causaba confusión.

Pero, ¿y si ya tuviéramos una variable declarada con el mismo nombre en otro lugar y la volvemos a declarar sin darnos cuenta? Entonces podríamos sobrescribir el valor de la variable, causando algunos problemas difíciles de depurar.

Usando la palabra clave let, si intentas volver a declarar la variable con el mismo nombre obtendrás un error – lo cual es bueno.

// Código ES6
let valor = 10;
console.log(valor); // 10

let valor = "hola"; // Uncaught SyntaxError: Identifier 'valor' has already been declared

Pero, el siguiente código es válido:

// Código ES6
let valor = 10;
console.log(valor); // 10

valor = "hola";
console.log(valor); // hola

No obtenemos un error en el código anterior porque estamos re-asignando un nuevo valor a la variable valor. Pero no estamos re-declarando valor de nuevo.

Ahora, observa el siguiente código:

// Código ES5
var esValido = true;
if(esValido) {
  var numero = 10;
  console.log('dentro:', numero); // dentro: 10
}
console.log('fuera:', numero); // fuera: 10

Como puedes ver en este código, cuando declaramos una variable con la palabra clave var, esta se encuentra disponible dentro y fuera del bloque if.

Ahora, observa el siguiente código:

// ES6 Code
let esValido = true;
if(esValido) {
  let numero = 10;
  console.log('dentro:', numero); // dentro: 10
}

console.log('fuera:', numero); // Uncaught ReferenceError: numero is not defined

Como puedes ver, al declarar la variable numero usando la palabra clave let esta solo es accesible dentro del bloque if. No está disponible fuera del bloque, por lo que obtuvimos un error de referencia cuando intentamos acceder a ella fuera del bloque if.

Pero si hay una variable numero fuera del bloque if, funcionará como se muestra a continuación:

// Código ES6
let esValido = true;
let numero = 20;

if(esValido) {
  let numero = 10;
  console.log('dentro:', numero); // dentro: 10
}

console.log('fuera:', numero); // fuera: 20

En este caso tenemos dos variables numero con un ámbito distinto. Por lo que fuera del bloque if, el valor numero será 20.

Observa el siguiente código:

// Código ES5
for(var i = 0; i < 10; i++){
 console.log(i);
}
console.log('fuera:', i); // 10

Al usar la palabra clave var, i está disponible incluso fuera del bucle for.

// Código ES6
for(let i = 0; i < 10; i++){
 console.log(i);
}

console.log('fuera:', i); // Uncaught ReferenceError: i is not defined

En cambio al usar let, esta no está disponible fuera del bucle.

Entonces como pudiste ver en los ejemplos de código anteriores, usar let hace la variable accesible solo dentro de ese bloque y no desde fuera del mismo.

También podemos crear un bloque con un par de llaves como esta:

let i = 10;
{
 let i = 20;
 console.log('dentro:', i); // dentro: 20
 i = 30;
 console.log('i de nuevo:', i); // i de nuevo: 30
}

console.log('fuera:', i); // fuera: 10

Si recuerdas, comenté que no podemos re-declarar una variable basada en let en el mismo bloque, pero podemos re-declararla en otro bloque. Como puedes ver en el código anterior, re-declaramos i y asignamos un nuevo valor de 20 dentro del bloque. Una vez declarada, ese valor de la variable estará disponible solo en ese bloque.

Fuera del bloque, cuando imprimimos esa variable, obtuvimos 10 en lugar de 30 que fue el valor previamente asignado, ya que fuera del bloque la variable interior i no existe.

Si no tenemos la variable i declarada afuera, obtendremos un error como puedes ver en el siguiente código:

{
 let i = 20;
 console.log('dentro:', i); // dentro: 20
 i = 30;
 console.log('i de nuevo:', i); // i de nuevo: 30
}

console.log('fuera:', i); // Uncaught ReferenceError: i is not defined

Como usar const en JavaScript

La palabra clave const funciona exactamente igual que la palabra clave let en cuanto a su ámbito de bloque. Así que veamos cómo se diferencian entre sí.

Cuando declaramos una variable como const, esta se considera una variable constante cuyo valor nunca cambiará.

En el caso de let, podemos asignar mas adelante un nuevo valor a esa variable de esta manera:

let numero = 10;
numero = 20;

console.log(numero); // 20

Pero no podemos hacer eso en el caso de const:

const numero = 10;
numero = 20; // Uncaught TypeError: Assignment to constant variable.

Ni siquiera podemos re-declarar una variable const.

const numero = 20;
console.log(numero); // 20

const numero = 10; // Uncaught SyntaxError: Identifier 'numero' has already been declared

Ahora, observa el siguiente código:

const arr = [1, 2, 3, 4];

arr.push(5);

console.log(arr); // [1, 2, 3, 4, 5]

Hemos dicho que la variable const es una constante cuyo valor nunca cambiará – pero acabamos de hacer un cambio en un arreglo definido como constante. Entonces, ¿qué sentido tiene eso?

Nota: En JavaScript los arreglos son de tipo referencia y no de tipo primitivo.

Así que lo que realmente se almacena en arr no es el arreglo real, sino una referencia (dirección) de la ubicación en la memoria donde se encuentra almacenado el arreglo real.

Entonces haciendo arr.push(5); no estamos cambiando la referencia a la que apunta arr, pero estamos cambiando los valores almacenados en ella.

Con objetos tenemos el mismo caso:

const obj = {
 nombre: 'David',
 edad: 30
};

obj.edad = 40;

console.log(obj); // { nombre: 'David', edad: 40 }

Aquí tampoco estamos cambiando la referencia donde apunta obj, sino los valores almacenados en ella.

De tal manera que el código anterior funcionará, pero el siguiente no.

const obj = { nombre: 'David', edad: 30 };
const obj1 = { nombre: 'Mike', edad: 40 };
obj = obj1; // Uncaught TypeError: Assignment to constant variable.

El código anterior no funciona porque estamos intentando cambiar la referencia a la que apunta la variable const.

Así que el punto clave a recordar cuando usamos const es que, si declaramos la variable como constante, no podemos re-definirla. Tampoco podemos re-asignar esa variable, pero podemos cambiar los valores almacenados en la misma si esta es de tipo referencia.

Por lo tanto, el siguiente código no es válido porque le estamos reasignando un nuevo valor.

const arr = [1, 2, 3, 4];
arr = [10, 20, 30]; // Uncaught TypeError: Assignment to constant variable.

Pero ten en cuenta que podemos cambiar los valores dentro del arreglo, como vimos anteriormente.

El siguiente código para re-definir una variable const también es inválido.

const nombre = "David";
const nombre = "Raj"; // Uncaught SyntaxError: Identifier 'nombre' has already been declared

Resumen de let y const

  • Las palabras claves let y const añaden ámbito de bloque en JavaScript.
  • Cuando declaramos una variable como let, no podemos re-definir o re-declarar otra variable let con el mismo nombre en el mismo ámbito (funcional o de bloque). Pero podemos re-asignar un valor a ella.
  • Cuando declaramos una variable como const, no podemos re-definir o re-declarar otra variable const con el mismo nombre en el mismo ámbito (funcional o de bloque). Pero podemos cambiar los valores almacenados en dicha variable, si esta es de tipo referencia, como un arreglo u objeto.

Muy bien, pasemos al siguiente gran tema: promesas.

Promesas en JavaScript

Las promesas son una de las partes más importantes, aunque confusas y difíciles de entender de JavaScript. Y la mayoría de los desarrolladores nuevos, así como los experimentados, luchan por comprenderlas.

Las promesas se añadieron en ES6 de forma nativa.

Entonces, ¿qué es una promesa? Una promesa representa una operación asincrónica que se completará en el futuro.

Anteriormente, antes de ES6, no había forma de esperar por algo que realizara alguna operación.

Por ejemplo, cuando queríamos hacer una llamada a la API, no había forma de esperar hasta que los resultados llegaran.

Para eso, solíamos usar bibliotecas externas como Jquery o Ajax que tenían su propia implementación de promesas. Pero el navegador no contaba nativamente con dichas funciones.

Pero ahora, usando Promesas en ES6, podemos hacer una llamada a la API por nuestra cuenta y esperar hasta que esté lista para realizar alguna operación.

Como crear una Promesa

Para crear una promesa, necesitamos usar la función constructora Promise de la siguiente forma:

const promesa = new Promise(function(resolver, rechazar) {
 
});

El constructor Promise toma una función como argumento y dicha función recibe internamente resolver y rechazar como parámetros.

Los parámetros resolver y rechazar son en realidad funciones que podemos llamar dependiendo del resultado de la operación asincrónica.

Una promesa Promise pasa por tres estados:

  • Pendiente
  • Cumplido
  • Rechazado

Cuando creamos una promesa, la misma está en estado pendiente. Al llamar a la función resolver, entra en estado cumplido y si llamamos rechazar, pasará al estado rechazado.

Para simular una operación asíncrona o de larga duración, usaremos la función setTimeout.

const promesa = new Promise(function(resolver, rechazar) {
 setTimeout(function() {
  const suma = 4 + 5;
  resolver(suma);
 }, 2000);
});

Aquí creamos una promesa que se resolverá con la suma de 4 y 5 después de que finalice un tiempo de espera de 2000 ms (2 segundos).

Para obtener el resultado de la ejecución exitosa de la promesa, necesitamos registrar un callback usando .then de la siguiente manera:

const promesa = new Promise(function(resolver, rechazar) {
 setTimeout(function() {
  const suma = 4 + 5;
  resolver(suma);
 }, 2000);
});

promesa.then(function(resultado) {
 console.log(resultado); // 9
});

Entonces, cada vez que llamemos resolver, la promesa devolverá el valor transmitido a la función, el cual podemos recopilar usando el manejador .then.

Si la operación no tiene éxito, llamamos a la función rechazar así:

const promesa = new Promise(function(resolver, rechazar) {
 setTimeout(function() {
  const suma = 4 + 5 + 'a';
  if(isNaN(suma)) {
    rechazar('Error al calcular la suma.');
  } else {
    resolver(suma);
  }
 }, 2000);
});

promesa.then(function(resultado) {
 console.log(resultado);
});

Aquí, si la suma no es un número, llamamos a la función rechazar con el mensaje de error. De lo contrario, llamamos a la función resolver.

Si ejecutas el código anterior, verás el siguiente resultado:

Error without catch

Como puedes ver, obtenemos un mensaje de error sin captar junto al mensaje que especificamos, porque llamar a la función rechazar arroja un error. Pero no hemos agregado un manejador de errores para captar dicho error.

Para captar el error, necesitamos registrar otro callback usando .catch de esta forma:

promesa.then(function(resultado) {
 console.log(resultado);
}).catch(function(error) {
 console.log(error);
});

Verás la siguiente salida:

Error with catch

Como puedes ver, hemos agregado el manejador .catch, por lo que no obtenemos ningún error sin captar, solo estamos registrando el error en la consola.

Esto también evita que tu aplicación se detenga abruptamente.

Por lo tanto, se recomienda siempre agregar el manejador .catch a cada promesa para que la aplicación no deje de ejecutarse debido a un error.

Encadenamiento de promesas

Podemos agregar multiples manejadores .then a una sola promesa de la siguiente manera:

promesa.then(function(resultado) {
 console.log('Primer manejador .then');
 return resultado;
}).then(function(resultado) {
 console.log('Segundo manejador .then');
 console.log(resultado);
}).catch(function(error) {
 console.log(error);
});

Cuando tenemos agregados múltiples manejadores .then, el valor devuelto por el anterior es traspasado automáticamente al siguiente.

Promise Chaining

Como puedes ver, al sumar 4 + 5 se resuelve una promesa, y obtenemos el resultado de la suma en el primer manejador .then. Allí imprimimos un mensaje en la consola y devolvemos el resultado al siguiente .then.

Dentro del siguiente manejador .then, agregamos una declaración de consola e imprimimos el resultado que obtuvimos del manejador .then anterior.

Este patrón de agregar múltiples manejadores .then de forma consecutiva se conoce como encadenamiento de promesas.

Cómo retrasar la ejecución de una promesa en JavaScript

Muchas veces no queremos crear una promesa de inmediato, sino que queremos crear una después de que se complete alguna operación.

Para lograr esto, podemos envolver la promesa en una función y devolver esa promesa desde dicha función de esta manera:

function crearPromesa() {
 return new Promise(function(resolver, rechazar) {
  setTimeout(function() {
   const suma = 4 + 5;
   if(isNaN(suma)) {
     rechazar('Error al calcular la suma.');
   } else {
    resolver(suma);
   }
  }, 2000);
 });
}

De esta manera, podemos usar los parámetros de la función dentro de la promesa, haciendo que la función sea verdaderamente dinámica.

function crearPromesa() {
 return new Promise(function(resolver, rechazar) {
  setTimeout(function() {
   const suma = 4 + 5;
   if(isNaN(suma)) {
     rechazar('Error al calcular la suma.');
   } else {
    resolver(suma);
   }
  }, 2000);
 });
}

crearPromesa(1,8)
 .then(function(resultado) {
  console.log(resultado); // 9
});

// OR

crearPromesa(10,24)
 .then(function(resultado) {
  console.log(resultado); // 34
});
Output

Nota: Cuando creamos una promesa, se resolverá o rechazará, pero no ambas al mismo tiempo. Por lo tanto, no podemos agregar dos llamadas a la función resolver o rechazar en la misma promesa.

Además, podemos pasar solo un valor a la función resolver o rechazar.

Si deseas pasar varios valores a una función resolver, puedes utilizar un objeto como en el siguiente ejemplo:

const promesa = new Promise(function(resolver, rechazar) {
 setTimeout(function() {
  const suma = 4 + 5;
  resolver({
   a: 4,
   b: 5,
   suma
  });
 }, 2000);
});

promesa.then(function(resultado) {
 console.log(resultado);
}).catch(function(error) {
 console.log(error);
});
Resolving object

Cómo usar las funciones de flecha en JavaScript

En todos los ejemplos de código pasados, al crear promesas utilizamos la sintaxis regular de función en ES5. Pero en su lugar, es común usar la sintaxis de función de flecha de la siguiente manera.

const promesa = new Promise((resolver, rechazar) => {
 setTimeout(() => {
  const suma = 4 + 5 + 'a';
  if(isNaN(suma)) {
    rechazar('Error al calcular la suma.');
  } else {
    resolver(suma);
  }
 }, 2000);
});

promesa.then((resultado) => {
 console.log(resultado);
});

Puedes utilizar la sintaxis de función en ES5 o ES6 dependiendo de tus preferencias y necesidades.

Sintaxis para Import y Export en ES6

Antes de que ES6 entrara en juego, utilizabamos múltiples etiquetas script en un solo archivo HTML para importar diferentes archivos JavaScript de esta manera:

<script type="text/javascript" src="inicio.js"></script>
<script type="text/javascript" src="perfil.js"></script>
<script type="text/javascript" src="usuario.js"></script>

Entonces, si existiese una variable con el mismo nombre en diferentes archivos JavaScript, se crearía un conflicto de nombres y el valor que esperabas no sería el valor real que obtuviste.

ES6 ha solucionado este problema con el concepto de módulos.

Cada archivo JavaScript que escribimos en ES6 se conoce como módulo. Las variables y funciones que declaramos en cada archivo no están disponibles para otros archivos hasta que las exportamos específicamente desde ese archivo y las importamos a otro.

Por lo tanto, las funciones y variables definidas en el archivo son privadas para cada uno, y no se pueden acceder desde fuera hasta que las exportemos.

Hay dos tipos de exportaciones:

  • Exportaciones Nombradas: Pueden haber múltiples exportaciones con nombre en un solo archivo
  • Exportaciones por Defecto: Solo puede haber una exportación por defecto en un solo archivo

Exportaciones Nombradas en JavaScript

Para exportar un solo valor como una exportación nombrada, lo exportamos así:

export const temp = "Este es un texto ficticio";

Si tenemos varias cosas para exportar, podemos escribir una declaración de exportación en una línea separada en lugar de delante de la declaración de variable. Especificamos las cosas a exportar entre llaves.

const temp1 = "Este es un texto ficticio1";
const temp2 = "Este es un texto ficticio2";

export { temp1, temp2 };

Ten en cuenta que la sintaxis de exportación no es una sintaxis literal de objeto. Por lo que, en ES6, para exportar algo no podemos usar pares clave-valor como el siguiente:

 // Esta es una sintaxis de exportación no válida en ES6

export { clave1: valor1, clave2: valor2 }

Para importar las cosas que exportamos como una exportación nombrada, usamos la siguiente sintaxis:

import { temp1, temp2 } from './nombreDelArchivo';

Ten en cuenta que al importar algo desde un archivo, no es necesario agregar la extensión .js al nombre del mismo, ya que se considera de forma predeterminada.

// importar desde el archivo funciones.js desde el directorio actual 
import { temp1, temp2 } from './funciones';

// importar desde el archivo funciones.js desde el padre del directorio actual
import { temp1 } from '../funciones';

Aquí hay una demostración en Code Sandbox: https://codesandbox.io/s/hardcore-pond-q4cjx

Una cosa a tener en cuenta es que el nombre utilizado durante la exportación debe coincidir con el nombre que utilizamos durante la importación.

Por lo que, si estás exportando así:

// constantes.js
export const PI = 3.14159;

Luego, al importar, debes usar el mismo nombre que usaste al exportar:

import { PI } from './constantes';

No puedes usar ningún otro nombre como este:

import { ValorPI } from './constantes'; // Esto arrojará un error

Pero si ya tienes la variable con el mismo nombre que la variable exportada, puedes usar la siguiente sintaxis para renombrar la variable mientras realizas la importación:

import { PI as ValorPI } from './constantes';

Aquí hemos cambiado el nombre de PI a ValorPI y por lo tanto ya no podemos usar el nombre PI como variable. En su lugar, tenemos que usar la variable ValorPI para obtener el valor exportado de PI.

También podemos utilizar la sintaxis de cambio de nombre a la hora de exportar:

// constantes.js
const PI = 3.14159; 

export { PI as ValorPI };

Luego, al importar, tenemos que usar ValorPI así:

import { ValorPI } from './constantes';

Para exportar algo como una exportación nombrada, primero debemos declararlo.

export 'hola'; // Esto arrojará un error
export const saludos = 'hola'; // Esto funcionará
export { nombre: 'David' }; // Esto arrojará un error
export const objeto = { nombre: 'David' }; // Esto funcionará

El orden en el que importamos las múltiples exportaciones nombradas no es importante.

Fíjate en el siguiente archivo validations.js:

// utils/validations.js

const esEmailValido = function(email) {
  if (/^[^@ ]+@[^@ ]+\.[^@ \.]{2,}$/.test(email)) {
    return "el email es válido";
  } else {
    return "el email es inválido";
  }
};

const esTelefonoValido = function(telefono) {
  if (/^[\\(]\d{3}[\\)]\s\d{3}-\d{4}$/.test(telefono)) {
    return "el número de teléfono es válido";
  } else {
    return "el número de teléfono es inválido";
  }
};

function estaVacia(valor) { 
  if (/^\s*$/.test(valor)) {
    return "la cadena está vacía o contiene solo espacios";
  } else {
    return "la cadena no está vacía y no contiene espacios";
  } 
}

export { esEmailValido, esTelefonoValido, estaVacia };

Y en index.js usamos estas funciones como se muestra a continuación:

// index.js
import { estaVacia, esEmailValido } from "./utils/validations";

console.log("estaVacia:", estaVacia("abcd")); // estaVacia: la cadena no está vacía y no contiene espacios

console.log("esEmailValido:", esEmailValido("abc@11gmail.com")); // esEmailValido: el email es válido

console.log("esEmailValido:", esEmailValido("ab@c@11gmail.com")); // esEmailValido: el email es inválido

Aquí hay una demostración en Code Sandbox: https://codesandbox.io/s/youthful-flower-xesus

Como puedes ver, pudimos importar solo los elementos exportados requeridos y en cualquier orden, por lo que no es necesario verificar en qué orden fueron exportados en otro archivo. Esa es la belleza de las exportaciones nombradas.

Exportaciones por Defecto en JavaScript

Como dije anteriormente, puede haber como máximo una exportación por defecto en un solo archivo.

Sin embargo, puedes combinar varias exportaciones nombradas y una exportación por defecto en un solo archivo.

Para declarar una exportación por defecto, agregamos la palabra clave default  delante de la palabra clave export de la siguiente manera:

//constantes.js
const nombre = 'David'; 
export default nombre;

Para importar la exportación por defecto, no agregamos llaves como hicimos en la exportación nombrada, en cambio lo hacemos así:

import nombre from './constantes';

Si tenemos varias exportaciones nombradas y una exportación por defecto como esta:

// constantes.js
export const PI = 3.14159; 
export const EDAD = 30;

const NOMBRE = "David";
export default NOMBRE;

Luego, para importar todo en una sola línea, necesitamos usar la variable exportada por defecto antes del corchete.

// NOMBRE es una exportación por defecto y PI y Edad son exportaciones nombradas

import NOMBRE, { PI, EDAD } from './constantes';

Una especialidad de la exportación por defecto es que podemos cambiar el nombre de la variable exportada durante la importación:

// constantes.js
const EDAD = 30;
export default EDAD;

Y en otro archivo, podemos usar otro nombre al importar

import miEdad from ‘./constantes’; 

console.log(miEdad); // 30

Aquí, hemos cambiado el nombre de la variable exportada por defecto de EDAD a miEdad.

Esto funciona porque solo puede haber una exportación por defecto, por lo que puedes nombrarla como desees.

Otra cosa a tener en cuenta sobre la exportación por defecto es que la palabra clave export default no puede ir antes de la declaración de una variable como esta:

// constantes.js
export default const EDAD = 30; // Este es un error y no funcionará

Así que tenemos que usar la palabra clave export default en una línea separada de esta forma:

// constantes.js 

const EDAD = 30; 
export default EDAD;

Sin embargo, podemos exportar por defecto sin declarar la variable de esta manera:

//constantes.js
export default {
 nombre: "Billy",
 edad: 40
};

Y en otro archivo usarlo así:

import usuario from './constantes';
console.log(usuario.nombre); // Billy 
console.log(usuario.edad); // 40

Existe otra forma de importar todas las variables exportadas en un archivo usando la siguiente sintaxis:

import * as constantes from './constantes';

Aquí, estamos importando todas las exportaciones nombradas y por defecto que tenemos en constantes.js y estas se almacenaran en la variable constantes. Así que, constantes se convertirá en un objecto.

// constantes.js
export const USERNAME = "David";
export default {
 nombre: "Billy",
 edad: 40
};

Y en otro archivo, lo usamos de la siguiente manera:

// prueba.js

import * as constantes from './constantes';

console.log(constantes.USERNAME); // David
console.log(constantes.default); // { nombre: "Billy", edad: 40 }
console.log(constantes.default.edad); // 40

Aquí hay una demostración en Code Sandbox: https://codesandbox.io/s/green-hill-dj43b

Si no deseas exportar en líneas separadas las exportaciones por defecto y nombradas, puedes combinarlas como se muestra a continuación:

// constantes.js
const PI = 3.14159; const EDAD = 30;
const USERNAME = "David";
const USUARIO = {
 nombre: "Billy",
 edad: 40 
};

export { PI, EDAD, USERNAME, USUARIO as default };

Aquí, estamos exportando USUARIO como exportación por defecto y las otras como exportaciones nombradas.

En otro archivo, puedes usarlo así:

import USUARIO, { PI, EDAD, USERNAME } from "./constantes";

Aquí hay una demostración en Code Sandbox: https://codesandbox.io/s/eloquent-northcutt-7btp1

En resumen:

  1. En ES6, los datos declarados en un archivo no son accesibles a otro archivo hasta que se exportan desde ese archivo y se importan a otro archivo.
  2. Si tenemos una sola cosa para exportar en un archivo como una declaración de clase, usamos la exportación por defecto, de lo contrario usamos la exportación nombrada. También podemos combinar exportaciones por defecto y nombradas en un solo archivo.

Parámetros Predeterminados en JavaScript

ES6 ha agregado una característica bastante útil para proporcionar parámetros predeterminados al definir funciones.

Supongamos que tenemos una aplicación, donde una vez que el usuario inicia sesión en el sistema, les mostramos un mensaje de bienvenida como este:

function mostrarMensaje(primerNombre) {
  return "Bienvenido de nuevo, " + primerNombre;
}
console.log(mostrarMensaje('John')); // Bienvenido de nuevo, John

Pero, ¿qué pasa si no tenemos el nombre de usuario en nuestra base de datos, ya que era un campo opcional al momento de registrarse? Entonces podemos mostrar un mensaje al usuario como: Bienvenido Invitado después de iniciar sesión.

Así que, primero debemos verificar si se proporciona el primerNombre y luego mostrar el mensaje correspondiente. Antes de ES6, habríamos tenido que escribir un código como este:

function mostrarMensaje(primerNombre) {
  if(primerNombre) {
    return "Bienvenido de nuevo, " + primerNombre;
  } else {
    return "Bienvenido de nuevo, Invitado";
  }
}

console.log(mostrarMensaje('John')); // Bienvenido de nuevo, John 
console.log(mostrarMensaje()); // Bienvenido de nuevo, Invitado

Pero ahora en ES6 usando los parámetros de función predeterminados, podemos escribir el código anterior como se muestra a continuación:

function mostrarMensaje(primerNombre = 'Invitado') {
   return "Bienvenido de nuevo, " + primerNombre;
}

console.log(mostrarMensaje('John')); // Bienvenido de nuevo, John 
console.log(mostrarMensaje()); // Bienvenido de nuevo, Invitado

Podemos asignar cualquier valor como predeterminado al parámetro de la función.

function mostrar(a = 10, b = 20, c = b) { 
 console.log(a, b, c);
}

mostrar(); // 10 20 20
mostrar(40); // 40 20 20
mostrar(1, 70); // 1 70 70
mostrar(1, 30, 70); // 1 30 70

Como puedes ver, hemos asignado valores únicos a los parámetros de la función a y b, pero para c estamos asignando el valor de b. Entonces, cualquier valor que hayamos proporcionado para b se asignará a c también si no se proporciona un valor específico para c al llamar a la función.

En el código anterior, no hemos proporcionado todos los argumentos a la función. Entonces, las llamadas a las funciones anteriores serán las mismas que las siguientes:

mostrar(); // es igual que mostrar(undefined, undefined, undefined)
mostrar(40); // es igual que mostrar(40, undefined, undefined)
mostrar(1, 70); // es igual que mostrar(1, 70, undefined)

De tal manera que, si el argumento pasado es undefined, el valor predeterminado se usará para el parámetro correspondiente.

También podemos asignar valores complejos o calculados como valor predeterminado.

const usuarioPredeterminado = {
  nombre: 'Jane',
  ubicacion: 'NY',
  trabajo: 'Desarrolladora de Software'
};

const mostrar = (usuario = usuarioPredeterminado, edad = 60 / 2 ) => { 
 console.log(usuario, edad);
};
mostrar();

/* resultado:

{
  nombre: 'Jane',
  ubicacion: 'NY',
  trabajo: 'Software Developer'
} 30 

*/

Ahora, da un vistazo al siguiente código en ES5:

// Código ES5
function obtenerUsuarios(pagina, resultados, genero, nacionalidad) {
  var parametros = "";
  if(pagina === 0 || pagina) {
   parametros += `page=${pagina}&`; 
  }
  if(resultados) {
   parametros += `results=${resultados}&`;
  }
  if(genero) {
   parametros += `gender=${genero}&`;
  }
  if(nacionalidad) {
   parametros += `nationality=${nacionalidad}`;
  }

  fetch('https://randomuser.me/api/?' + parametros) 
   .then(function(respuesta) {
     return respuesta.json(); 
   })
   .then(function(resultado) { 
    console.log(resultado);
   }) 
   .catch(function(error) {
     console.log('error', error); 
   }); 
}

obtenerUsuarios(0, 10, 'male', 'us');

En este código, estamos haciendo una llamada API al usuario aleatorio (Random user) pasando varios parámetros opcionales en la función obtenerUsuarios.

Entonces, antes de realizar la llamada API, hemos agregado varias condiciones if para verificar si el parámetro se agrega o no, y en base a eso, construimos una cadena de consulta de la siguiente manera: https://randomuser.me/api/?page=0&results=10&gender=male&nationality=us.

Pero en lugar de agregar tantas condiciones if, podemos usar los parámetros predeterminados mientras definimos los parámetros de la función como se muestra a continuación:

function obtenerUsuarios(pagina = 0, resultados = 10, genero = 'male', nacionalidad = 'us') {
 fetch(`https://randomuser.me/api/?page=${pagina}&results=${resultados}&gender=${genero}&nationality=${nacionalidad}`)
 .then(function(respuesta) { 
  return respuesta.json();
 }) 
 .then(function(resultado) {
   console.log(resultado); 
 })
 .catch(function(error) { 
  console.log('error', error);
  }); 
}

obtenerUsuarios();

Como puedes ver, hemos simplificado mucho el código. Entonces, cuando no proporcionamos ningún argumento a la función obtenerUsuarios, esta tomará valores predeterminados. También podemos proporcionar nuestros propios valores de esta forma:

obtenerUsuarios(1, 20, 'female', 'gb');

Lo cual sobrescribirá los parámetros predeterminados de la función.

null no es igual a undefined

Pero debes tener en cuenta algo importante: null y undefined son dos cosas diferentes al definir parámetros predeterminados.

Observa el siguiente código:

function mostrar(nombre = 'David', edad = 35, ubicacion = 'NY'){
 console.log(nombre, edad, ubicacion); 
}

mostrar('David', 35); // David 35 NY
mostrar('David', 35, undefined); // David 35 NY

Como no hemos proporcionado el tercer valor para el parámetro de ubicación en la primera llamada a mostrar, este será undefined por defecto, por lo dicho valor undefined se utilizará en ambas llamadas de función. Sin embargo, las siguientes llamadas a funciones no son iguales.

display('David', 35, undefined); // David 35 NY
display('David', 35, null); // David 35 null

Cuando pasamos null como argumento, estamos diciendo específicamente que asignemos un valor null (nulo) al parámetro de ubicacion lo cual es diferente que undefined (indefinido). Por lo tanto, no tomará el valor predeterminado de NY.

Array.prototype.includes

ES7 ha agregado una nueva función que verifica si un elemento está presente en el arreglo o no y devuelve un valor booleano true o false.

// Código ES5

const numeros = ["uno", "dos", "tres", "cuatro"];

console.log(numeros.indexOf("uno") > -1); // true 
console.log(numeros.indexOf("cinco") > -1); // false

El mismo código usando el método Array includes puede escribirse como se muestra a continuación:

// Código ES7

const numeros = ["uno", "dos", "tres", "cuatro"];

console.log(numeros.includes("uno")); // true 
console.log(numeros.includes("cinco")); // false

Por lo tanto, el uso del método Array includes hace que el código sea corto y fácil de entender.

El método includes también es útil cuando se comparan diferentes valores.

Observa el siguiente código:

const dia = "lunes";

if(dia === "lunes" || dia === "martes" || dia === "miercoles") {
  // hacer algo
}

El código anterior se puede simplificar usando el método includes como se muestra a continuación:

const dia = "lunes";

if(["lunes", "martes", "miercoles"].includes(dia)) {
  // hacer algo
}

Así que, el método includes es bastante útil cuando se buscan valores en un arreglo.

Puntos finales

Hay muchos cambios que se han incorporado a JavaScript a partir de ES6. Y todos los desarrolladores de JavaScript, Angular, React o Vue deberían estar al tanto de ellos.

Conocerlos te convierte en un mejor desarrollador e incluso puede ayudarte a conseguir un trabajo mejor remunerado. Y si solo estás aprendiendo librerías como React y frameworks como Angular y Vue, seguramente querrás estar familiarizado con estas nuevas funcionalidades.

Aprende más sobre las funcionalidades de JavaScript moderno

Puedes aprender todo sobre las últimas funcionalidades agregadas en JavaScript en mi libro Mastering Modern JavaScript. Es la única guía que necesitas para aprender conceptos modernos de JavaScript.

book_cover

Suscríbete a mi boletín semanal para unirte a más de 1000 suscriptores y obtener consejos, trucos y artículos increíbles directamente en tu bandeja de entrada.

Traducido del artículo de Yogesh Chavan - Modern JavaScript – Imports, Exports, Let, Const, and Promises in ES6+