Introducci贸n
Contenido
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.