Operador de propagación en JavaScript

O

Introducción

En este tutorial, exploraremos una de las poderosas características de la especificación ES6 de JavaScript: el operador de propagación. Aunque la sintaxis es simple, a veces la implementación es confusa si no la comprende correctamente. En este tutorial, desmitificaremos esos tres puntos ... de JavaScript que hace cosas increíbles con iterables.

Usos del operador de propagación

Existen diferentes usos del operador de propagación y cada objetivo de uso para resolver un enunciado de problema diferente.

Expansión de matrices

Podemos usar el operador de propagación en iterables como una cadena o una matriz y pondrá el contenido del iterable en elementos individuales.

Para un ejemplo:

let greet = ['Hello', 'World'];
console.log(greet); // Without spread operator
console.log(...greet); // Using spread operator

Si ejecutamos este código veremos lo siguiente:

['Hello', 'World']
Hello World

Debe haber notado que en el segundo caso (con operador de propagación), el contenido de la greet La lista se expandió y se descartó de la matriz.

A veces, podemos sentir la necesidad de convertir un String en una lista de personajes. Podemos usar el operador de propagación para este caso de uso:

let greetings = "hello";
let chars = [...greetings];
console.log(chars);

Si ejecutamos este código, seremos recibidos con:

[ 'h', 'e', 'l', 'l', 'o' ]

Es posible que estos ejemplos no lo convenzan de la utilidad que ofrece este operador. En ese nombre, tomemos algunos problemas del mundo real que se pueden resolver con los operadores de propagación.

Combinando matrices

Aprovechemos el hecho de que ahora podemos expandir una matriz usando el operador de extensión. Digamos que tenemos listas de suscriptores de dos fuentes diferentes y queremos combinar ambas fuentes y hacer una sola lista de suscriptores:

let blog1Subscribers = ['[email protected]', '[email protected]'];
let blog2Subscribers = ['[email protected]', '[email protected]', '[email protected]'];
let subscribers = [...blog1Subscribers, ...blog2Subscribers];
console.log(subscribers);

Si ejecutamos el código anterior, obtendremos una única lista de iterables. Esto fue posible ya que tanto ...blog1Subscribers y ...blog2Subscribers se extendieron y el [] actuó como el “receptor”, que combinó eficazmente los elementos de distribución en una única lista de elementos.

Nota: El operador de propagación necesita que el receptor ingrese el valor expandido. Si omite el receptor, arrojará un error.

También podemos usar el operador de propagación dentro del Array.push() método para empujar el contenido de una matriz a otra:

let arr1 = ['John', 'Sofia', 'Bob'];
let arr2 = ['Julia', 'Sean', 'Anthony'];
arr2.push(...arr2);
console.log(arr1);

Si ejecutamos este código, veremos el siguiente resultado:

[ 'Julia', 'Sean', 'Anthony', 'John', 'Sofia', 'Bob' ]

Copiar matrices y objetos

En JavaScript, cada entidad no primitiva es una Object, lo que significa que las matrices también son objetos. Puede saber que los objetos se copian como un tipo de referencia:

let arr1 = ['John', 'Sofia', 'Bob'];
let arr2 = arr1;
console.log(arr2);
arr1.push('Sally'); // Change arr1
console.log(arr2);
[ 'John', 'Sofia', 'Bob' ]
[ 'John', 'Sofia', 'Bob', 'Sally' ]

Como era de esperar, los valores de los elementos de la matriz no se copiaron, solo la referencia a ellos. Podemos resolver este problema fácilmente con el operador de propagación:

let arr1 = ['John', 'Sofia', 'Bob'];
let arr2 = [...arr1];
console.log(arr2);
arr1.push('Sally'); // Change arr1
console.log(arr2);

La ejecución de este código produce lo siguiente:

[ 'John', 'Sofia', 'Bob' ]
[ 'John', 'Sofia', 'Bob' ]

Como podemos ver, el arr2 no se pasó una referencia como antes, sino que se completó con los valores de arr1 como un objeto completamente nuevo. Así que incluso cuando arr1 cambios arr2 sigue siendo el mismo.

También podemos usar el operador de propagación para crear una copia de una matriz. y agregue nuevos elementos al mismo tiempo:

let arr1 = ['John', 'Sofia', 'Bob'];
let arr2 = [...arr1, 'Anthony', 'Sean'];
console.log(arr2);
['John', 'Sofia', 'Bob', 'Anthony', 'Sean']

Nota: El operador de propagación funciona con todos los iterables, incluidos los objetos.

Anteriormente, esto habría requerido una línea adicional de código para agregar los nuevos elementos a la nueva matriz.

De manera similar, podemos copiar objetos usando el operador de propagación:

let o1 = { a: 1, b: 2 };
let o2 = { c: 3, d: 4, ...o1 };
console.log(o2);
{ c: 3, d: 4, a: 1, b: 2 }

Como podemos ver, copiamos con éxito el objeto. o1 dentro o2.

Esta función tiene muchos casos de uso del mundo real. Por ejemplo, digamos que almacenamos la información de registro del usuario en un objeto. Podemos hacer una copia superficial de ese objeto y agregar más información al objeto copiado:

let user = { name: 'John', email: '[email protected]' };
let _user = { ...user, ...getSession(user) };
console.log(_user);
{ name: 'John', email: '[email protected]', 'token': 'abc123', 'expiresAt': 1565630480671 }

También es posible que necesitemos combinar la información de facturación y envío en uno:

const billing = { billingContact: '0987654321', billingAddress: 'street no 123, xyz city' };
const shipping = { shippingContact: '123456789', shippingAddress: 'street no 999, abc city' };
const custInfo = { ...billing, ...shipping };
console.log(custInfo);

Si ejecutamos este código, deberíamos recibir:

{
  billingContact: '0987654321',
  billingAddress: 'street no 123, xyz city',
  shippingContact: '123456789',
  shippingAddress: 'street no 999, abc city'
}

Aquí podría plantearse una pregunta. ¿Qué pasa si ambos objetos tienen algunas de las mismas propiedades?

En caso de conflicto de propiedades, gana la propiedad del último objeto. Veamos esto en un ejemplo:

const o1 = { a: 1, b: 2 };
const o2 = { b: 3, c: 4, ...o1};
console.log(o2);

Si ejecuta este código, debería ver lo siguiente:

{ b: 2, c: 4, a: 1 }

Como podemos ver las propiedades del segundo objeto o2 gana. Sin embargo, si ponemos el operador de propagación primero:

const o1 = { a: 1, b: 2 };
const o2 = { ...o1, b: 3, c: 4};
console.log(o2);
{ a: 1, b: 3, c: 4 }

Podemos ver que la propiedad de o1 gana, lo que tiene sentido ya que o2 es el último objeto.

Un caso de uso de esta función podría ser realizar asignaciones predeterminadas:

const userProvided = {
    name: 'Bil Smith',
    email: '[email protected]',
};
const defaultValues = {
    name: 'Unknown',
    address: 'Alien',
    phone: null,
    email: null
};
const userInfo = { ...defaultValues, ...userProvided };

Alternativa a las funciones de llamada con apply ()

Digamos que una función toma un argumento: una lista de calificaciones de los 5 mejores estudiantes de una clase. También tenemos una lista procedente de una fuente externa. Seguramente, podemos evitar pasar elementos individuales y en su lugar pasar la lista completa utilizando el apply() método:

myFun(m1, m2, m3, m4, m5) {
    // Do something
}

let marks = [10, 23, 83, -1, 92];
myFun.apply(undefined, arr);

Podemos deshacernos de lo confuso undefined argumento y hacer el código más limpio llamando a la función directamente con el operador de propagación:

myFun(m1, m2, m3, m4, m5) {
    // Do something
}

let marks = [10, 23, 83, -1, 92];
myFun(...marks);

Usar con funciones matemáticas

JavaScript tiene un Math objeto que contiene varios métodos para operar con un conjunto de datos, es decir, una lista de datos.

Digamos que queremos obtener el valor máximo de los primeros tres números de una lista:

let mylist = [10, 23, 83, -1, 92, -33, 76, 29, 76, 100, 644, -633];
Math.max(mylist[0], mylist[1], mylist[2]);

¿Qué pasa si queremos obtener el máximo de todos los números en una lista? ¿Qué pasa si la lista tiene n número de elementos? Seguro que no queremos mylist[0], mylist[1]... mylist[1000].

El operador de esparcido proporciona una solución más limpia:

let mylist = [10, 23, 83, -1, 92, -33, 76, 29, 76, 100, 644, -633];
Math.max(...mylist);

Nota: Dado que el operador de propagación funciona tanto con matrices como con objetos, a veces puede tener la tentación de mezclarlos y combinarlos. ¡No hagas eso! Por ejemplo, la siguiente acción resultará en un error:

let user = {name:'John', age:28, email:'[email protected]'};
let items = [...user];
TypeError: user is not iterable

Operador de propagación vs parámetro de reposo

Tanto el operador de propagación como el parámetro de descanso comparten la misma sintaxis, es decir, los tres puntos mágicos .... Pero se comportan exactamente opuestos entre sí. Como principiante, a veces esto puede resultar confuso. La conclusión para comprender el comportamiento es comprender el contexto en el que se está utilizando.

Como aprendimos, el operador de propagación expande el contenido de un iterable. Por el contrario, el operador rest recopila todos los elementos restantes en una matriz.

function doSum(...items) {
    let sum = 0;
    for (let item of items){
        sum += item;
    }
    return sum;
}

doSum(1);
doSum(1,2);
doSum(1, 2, 3, 4);

Si ejecutamos el código anterior, seremos recibidos con lo siguiente:

1
3
6
10

Como podemos ver, cada vez que los elementos restantes fueron recopilados por el parámetro Rest.

También podemos proporcionar distintas variables para algunos de los elementos y hacer que el parámetro rest recopile el resto de los elementos. La única condición es que el parámetro rest siempre debe ser el último parámetro de la función:

function doSum(times, ...items) {
    let sum = 0;
    for (let item of items){
        sum += item*times;
    }
    return sum;
}

doSum(1, 1);
doSum(2, 1, 2);
doSum(3, 1, 2, 3);

Si ejecutamos el código anterior, veremos lo siguiente:

1
6
18

Conclusión

Como podemos ver el operador de propagación ... es una característica realmente poderosa de la especificación ES6 de JavaScript. Podemos resolver fácilmente muchos de los problemas del mundo real utilizando este operador. Como aprendimos de los diversos ejemplos discutidos en este artículo, nos permite escribir menos código y hacer más.

En este artículo, cubrimos los usos comunes del operador de propagación. También discutimos el parámetro de descanso de aspecto similar, pero diferente. Tenga en cuenta que podría haber docenas de otros casos de uso dependiendo del problema.

 

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias para su correcto funcionamiento. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad