Mutable o Inmutable

3/27
Recursos

Aportes 16

Preguntas 2

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.

Recomiendo esta charla (en ingles) sobre este tema, super increíble: https://www.youtube.com/watch?v=Wo0qiGPSV-s&ab_channel=JSConf
.
Son 30 minutos valiosos 😃

No te quedo claro la clase lee esto.

En JS los datos asignados a una variable pueden ser de dos tipos:

Primitive type (undefined, null, boolean, number, string, symbol), Reference type (objects, arrays , functions).

Una de las diferencia entre estas dos, está en la forma como se almacenan estos datos en memoria, para ser más claro un ejemplo.

let name = 'Javier';

let name2 = name;

let person = {name: 'javier'};

let person2 = person;

Cuando creamos name js crea un espacio en memoria y guarda su valor, ahora cuando creamos name2 js continúa crea un nuevo espacio en memoria y asigna el mismo valor de la varible name de esta forma el valor de la variable name2 es totalmente independiente a name.

Ahora si creamos la variable person como un objeto que contiene un name, y si luego creamos otra variable person2 y le asignamos el mismo objeto person, aquí es donde la cosa cambia con respectos a los datos primitivos, en este caso js guardara el objeto person2 como una referencia o apuntador al objeto person, es decir que ambas variables apuntan al mismo objeto en memoria.

Ahora si entendamos Mutable o Inmutable.

Mutable: es algo que se puede cambiar o agregar.

Inmutable: es algo que no puede cambiar ni agregar.

Los valores primitivos en js son algo agregado donde solo se pueden reasignar y por lo tanto, todos estos valores son inmutables. Entendamos con un ejemplo.

console.log(name); //javier
console.log(name2); //javier

name2 = 'platzi';

console.log(name); //javier
console.log(name2); //platzi''

Si imprimimos name y name2, ambas nos dan javier, pero si reasignamos un valor de name2 y volvemos a imprimir ocurre que solo cambia el valor de name2, lo que demuestra que js guardas estás variables de forma separada, aun cuando el valor de name2 se copio de name. Por eso los valores primitivos son inmutables.

ahora hagamos lo mismo con los objetos.

console.log(person); //{name: 'javier'}
console.log(person2); //{name: 'javier'}

person2.name = 'platzi';

console.log(person); //{name: 'platzi'}
console.log(person2); //{name: 'platzi'}

Al inicio obtenemos las mismas propiedades, ahora cambiemos una de las valores de las propiedades y veremos que js cambio el valor tanto de person y peron2, esto debido a que person2 se creo haciendo referencia al objeto person, con reference type js crea una referencia al mismo objeto y el objeto permanece mutable.

ya que el mismo objeto es mutable se puede cambiar o se pueden agregar nuevas propiedades al mismo objeto.

En es6 se creo un operador de propagación que permirte copias un objeto de forma segura sin hacer referencia al mismo objeto y sería así.

let person2 = {...person}

Ahora vuelve a ver la clase y veras como todo es más claro y entendible.

Paso por referencia vs Paso por valor

Por tipos, los primitivos se pasan por valor y los objetos por referencia

Ejemplo gráfico:

Mutable es un tipo de variable que se puede cambiar. En JavaScript, solo los objetos (objects) y las matrices (arrays) son mutables, no valores primitivos.

(Puedes hacer que el nombre de una variable apunte a un nuevo valor, pero el valor anterior todavía se mantiene en la memoria. De ahí la necesidad de la recolección de basura).

Un objeto mutable es un objeto cuyo estado puede modificarse después de su creación.

Los inmutables son los objetos cuyo estado no se puede cambiar una vez creado el objeto.

Las cadenas y los números son inmutables. Entendamos esto con un ejemplo:

var immutableString = “Hola”;

// En el código anterior, se crea un nuevo objeto con valor de cadena.

immutableString = immutableString + “Mundo”;

// Ahora agregamos “Mundo” al valor existente.

Al agregar “immutableString” con un valor de cadena, ocurren los siguientes eventos:

Se recupera el valor existente de "immutableString"
"World" se agrega al valor existente de "immutableString"
El valor resultante luego se asigna a un nuevo bloque de memoria
El objeto "immutableString" ahora apunta al espacio de memoria recién creado
El espacio de memoria creado anteriormente ahora está disponible para la recolección de basura.

Spread Operator

Utilizando el spread operator, cogemos los valores de person y los insertamos directamente en X. Así evitamos anidar un abjeto dentro de otro
Con el spread operator, podemos sumar arrays, hacer copias, añadir nuevos elementos…

Casi siempre es mejor clonar una lista en lugar de mutarla. Nos ayuda a evitar Side Effects.

Con las copias tenemos el mismo contenido pero distinta referencia en memoria.

Te das cuenta de que aprendes cuando comprendes las referencias en memoria de js 😃

// Reto clase
        const todo = [
            {name: "Comprar pan", status: true},
            {name: "Ir al gym", status: false},
            {name: "Terminar proyecto", status: false},
            {name: "Estudiar en Platzi", status: true},
        ];

        const list = document.getElementById('todo');

        todo.forEach((item) => {
            list.innerHTML += `
            <li>
                <label><input type="checkbox" ${item.status ? `checked` : ''}>${item.name}</label>
            </li>
            `
        })

Estaba buscando esta respuesta desde hace rato … por fin, esto es MUY importante, sino van a haber choques en la memoria y van a pasar cosas muy raras con los resultados al usar las variables o en este caso los arrays

Características de una función pura:

  • El valor que retorna depende sólo de los argumentos de entrada, es decir. El valor de retorno no cambiará si los valores de entrada no cambian
  • No puede modificar nada que este fuera de su ámbito.

La copia es una referencia en memoria, solo copia los elementos cambiados, creando una nueva estructura de datos on referencias de memoria y nuevos elementos.

Cuando cambiamos el array original, hacemos una mutación(mutable) y cuando generamos un nuevo estado: Estamos creando una estructura de datos inmutable

Agregamos después de la etiqueta li, el input de tipo (type) checkbox para marcar las tareas realizadas.

<div id="homeworks"></div>
    <script>
        const tareas = [
            {materia:"Matematicas", tarea:"Algebra pag 2"},
            {materia:"Quimica", tarea:"electrones"},
            {materia:"programacion", tarea:"usar foreach"},
            {materia:"diseño", tarea:"retoque foto"},
        ];

        let homeworks = document.getElementById("homeworks");
        tareas.forEach(item2 => {
            homeworks.innerHTML += `
            <li>${item2.tarea} - materia: ${item2.materia}</li><input type="checkbox">`;
        });
    </script>

Recomiendo mucho este tutorial sobre el una pequeñisima intro al paradigma funcional y las funciones o metodos que ya nos provee el propio JS

https://www.youtube.com/watch?v=3eLx9syx8iI&t=948s

éste teacher es genial _

Si aun no les quedó claro este concepto, les recomiendo volver al curso Intermedio de Programación Orientada a Objetos y ver este video sobre Funcionamiento de Memoria en JavaScript. En resumen, los datos primitivos cuando se los copia, no hacen copias por referencia, mientras que las estructuras complejas, si hacen copias por referencia, por lo que puedes sin quieres afectar al mismo objeto. Si les interesa conocer mas a fondo como funciona esto, deberian tomar cursos de C y C++ ya que estos abordan términos como punteros y referencias.
Pero en resumen:

 class Person{
    constructor(name){
        this.name=name;
    }
}

let danny=new Person('danny');
let copy=danny; //danny apunta a copy y copy apunta a danny, cambiar valores en estos
//afecta al original y a la copia
//Cuando creo la copia y el original
console.log(danny);
console.log(copy);
//cambiar original
danny.name='juan';
console.log(copy);
//cambiar copia
copy.name='copia';
console.log(danny);

Mutable: que muta, que puede cambiar.
inmutable: que no muta, que no cambia.

Primitive type: number, string, boolean, undefined, null
Reference type object, array , function

let array = [1,2,3,4,5,6]

array.metodo(funcion) // [2,4,6]

si vemos nuestro array original, podemos ver que ha mutado …

console.log(array) // [2,4,6] 

esta bien si es lo que deseamos hacer, pero si quisiéramos evitar esto, podríamos copiar nuestro array original

let array = [1,2,3,4,5,6]
let copy = [...array]

console.log(array) // [1,2,3,4,5,6]
console.log(copy) // [1,2,3,4,5,6]

y modificar la copia

copy.metodo(funcion) // [2,4,6]

si miramos en consola, vemos que nuestro copia ha mutado, pero nuestro array original sigue igual

console.log(array) // [1,2,3,4,5,6]
console.log(copy) // [2,4,6]

esto podría ser una solución, pero estaríamos utilizando mas recursos en memoria y en ocasiones nuestro array original será un array muy grande y no es muy optimo realizar copias y abusar de los recursos de memoria.
podemos llamar un método sobre el array original y almacenar el resultado en una variable

let array = [1,2,3,4,5,6]
let arrayModificado = array.metodo(funcion)

si miramos en consola, podemos ver el nuevo array y nuestro array original igual

console.log(array) // [1,2,3,4,5,6]
console.log(arrayModificado) // [2,4,6]

pero que esta pasando aqui … ¿no seria lo mismo que realizar una copia y mutar la copia?, ¡no!, es diferente.
si hacemos console.table(array) podemos ver el índice y los valores del array original

índice - Valor 
0 - 1
1 - 2
2 - 3
3 - 4
4 - 5
5 - 6

lo que el método hace sobre el array original es realizar la función que le indiquemos, es este caso solo nos deja los indices que contengan elementos con números pares y los almacena dentro de la variable arrayModificado.
si hacemos console.table(arrayModificado)

índice - Valor 
0 - 2
1 - 4
2 - 6

¿seguiría siendo lo mismo?,¿realizamos una copia y mutamos la copia?, no exactamente.
si es cierto que retornamos una nuevo array y lo almacenamos en una variable (arrayModificado), pero los valores almacenados dentro de sus indices están haciendo referencia al array original.
una forma de verlo mas clara es como si tuviéramos esto console.table(arrayModificado)

índice - Valor 
0 - array[1]
1 - array[3]
2 - array[5]

si queremos comprobarlo, comparamos los valores y observamos que nos retorna true, esta haciendo referencia al mismo espacio en memoria

console.log(array[1] === arrayMofificado[0]) // true 
console.log(array[3] === arrayMofificado[1]) // true 
console.log(array[5] === arrayMofificado[2]) // true 

este nuevo array y sus valores en sus indices están haciendo referencia al array original, de esta forma no estaríamos mutando al array original ni realizando copias de el, desde el nuevo array estaríamos haciendo referencia a los valores del array original y eliminando los indices que tengan números impares.
todas estas instrucciones que queremos que ejecute, las indicamos en el cuerpo de la función que pasamos por parámetro al método.
.
.
podemos en un nuevo array hacer referencia a indices del array original y añadir o eliminar otros elementos, teniendo lo mejor de las formas anteriores, no copiando ni mutando el original, haciendo referencia al array original aprovechando mejor los recursos de la memoria y teniendo nuestro array original igual y un nuevo array con indices referenciados del array original.

Eso de un árbol y que solo cambia si algo cambio es el fundamento del Virtual DOM que usan React y Vue, por eso son tan rápidos. Angular utiliza el Incremental DOM, que esta basado en la memoria…