Operador de propagaci贸n en JavaScript

    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.

     

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *