Mutabilidad e inmutabilidad en JavaScript | Nicolás Garzón
Un valor es mutable cuando puede cambiar sin crear otra identidad. Trabajar de forma inmutable significa representar un cambio mediante un valor nuevo en lugar de modificar el anterior.
JavaScript
Copiar const product = {
name : "Keyboard" ,
price : 120 ,
} ;
product. price = 150 ; Aquí se muta el mismo objeto.
JavaScript
Copiar const updatedProduct = {
... product,
price : 150 ,
} ; JavaScript
Copiar let name = "Nicolás" ;
name. toUpperCase ( ) ;
console. log ( name) ; El string original no cambia. toUpperCase devuelve otro string.
JavaScript
Copiar name = name. toUpperCase ( ) ; La variable puede reasignarse, pero el valor primitivo anterior no fue modificado.
JavaScript
Copiar const user = {
profile : {
city : "Bogotá" ,
} ,
} ;
user. profile. city = "Medellín" ; La identidad exterior sigue siendo la misma.
JavaScript
Copiar const before = user;
user. profile. city = "Cali" ;
before === user; JavaScript
Copiar const cart = {
items : [ ] ,
} ;
cart. items. push ( "Keyboard" ) ;
cart = { } ; const evita reasignar el binding cart; no congela el objeto referenciado.
JavaScript
Copiar const original = {
status : "pending" ,
} ;
const alias = original;
alias. status = "completed" ;
console. log ( original. status) ; Ambos bindings apuntan al mismo objeto. La mutación es visible desde cualquiera de ellos.
Este comportamiento puede ser útil cuando el objeto representa estado compartido intencional, pero también puede producir cambios difíciles de rastrear.
No toda mutación tiene el mismo riesgo.
JavaScript
Copiar function createReport ( orders ) {
const report = [ ] ;
for ( const order of orders) {
report. push ( {
id : order. id,
total : order. total,
} ) ;
}
return report;
} report se muta dentro de la función, pero no era visible desde fuera antes de retornarlo. Esta mutación local es fácil de controlar.
JavaScript
Copiar function addOrder ( orders, order ) {
orders. push ( order) ;
} La función modifica una colección recibida desde fuera. Quien la llama debe conocer ese efecto.
JavaScript
Copiar function addOrder ( orders, order ) {
return [ ... orders, order] ;
} Antes de mutar, pregunta:
¿Quién creó este objeto?
¿Quién más conserva una referencia?
¿El contrato promete modificarlo?
¿Existe historial o comparación por identidad?
¿El cambio debe notificarse a otro sistema?
Una API con intención explícita puede mutar correctamente.
JavaScript
Copiar cart. addItem ( product) ; El nombre comunica que el carrito cambiará.
Una función de transformación suele comunicar otro contrato.
JavaScript
Copiar const updatedCart = addItem ( cart, product) ; JavaScript
Copiar const updatedUser = {
... user,
name : "Nico" ,
} ; JavaScript
Copiar const updatedItems = [
... items,
newItem,
] ; Para reemplazar un elemento:
JavaScript
Copiar const updatedProducts = products. map ( ( product ) =>
product. id === targetId
? { ... product, price : 150 }
: product,
) ; Solo el producto modificado recibe una identidad nueva. Los demás objetos pueden reutilizarse.
JavaScript
Copiar const updatedUser = {
... user,
profile : {
... user. profile,
city : "Cali" ,
} ,
} ; Cada nivel del camino que cambia necesita una copia nueva.
Esto se conoce como structural sharing : las ramas sin cambios se comparten; las ramas modificadas se reemplazan.
JavaScript
Copiar updatedUser. settings === user. settings;
updatedUser. profile === user. profile; Muchas herramientas detectan cambios mediante referencias.
JavaScript
Copiar previousUser === nextUser; Si mutas el mismo objeto:
JavaScript
Copiar user. name = "Nico" ;
setUser ( user) ; La referencia puede seguir siendo igual y el sistema puede no reconocer correctamente el cambio.
En React, una actualización típica crea una referencia nueva.
JavaScript
Copiar setUser ( ( currentUser ) => ( {
... currentUser,
name : "Nico" ,
} ) ) ; Esto no significa que React exija que todo el programa sea profundamente inmutable. Significa que el estado expuesto a su sistema de actualización debe respetar su contrato.
La inmutabilidad facilita conservar versiones.
JavaScript
Copiar const history = [
initialState,
secondState,
thirdState,
] ; Si cada estado anterior se mutara después, el historial dejaría de representar el pasado.
JavaScript
Copiar function applyDiscount ( product, percentage ) {
return {
... product,
price : product. price * ( 1 - percentage) ,
} ;
} La función no modifica product; devuelve otro objeto.
Esto facilita pruebas y razonamiento, pero no toda función necesita ser pura. Las operaciones de red, almacenamiento o UI producen efectos por naturaleza.
JavaScript
Copiar push
pop
shift
unshift
splice
sort
reverse
fill
copyWithinCrean otro array o valor:
JavaScript
Copiar map
filter
slice
concat
toSorted
toReversed
toSpliced
with La clasificación depende del método. Consulta su contrato en lugar de asumir por el nombre.
JavaScript
Copiar const sortedProducts = products. sort ( byPrice) ; sortedProducts y products son el mismo array ordenado.
JavaScript
Copiar const sortedProducts = [ ... products] . sort ( byPrice) ; JavaScript
Copiar const sortedProducts = products. toSorted ( byPrice) ; La primera copia es superficial; los productos interiores continúan siendo los mismos objetos.
JavaScript
Copiar Object. freeze ( configuration) ; Impide ciertos cambios directos en ese objeto, pero solo de forma superficial.
La inmutabilidad como estrategia es una decisión sobre cómo fluye el estado. Object.freeze es una restricción aplicada a un objeto concreto. Se profundiza en Object.freeze y Object.seal .
Copiar estructuras grandes sin necesidad puede tener costos de memoria y CPU.
Además, algunas APIs están diseñadas alrededor de mutación controlada:
Map.set.
Set.add.
DOM.
Canvas.
Buffers y typed arrays.
Objetos internos de una clase.
La pregunta correcta no es “¿mutación sí o no?”, sino “¿quién puede observar este cambio y está claramente definido?”.
JavaScript
Copiar function decreaseStock ( products, productId, quantity ) {
return products. map ( ( product ) => {
if ( product. id !== productId) {
return product;
}
return {
... product,
stock : product. stock - quantity,
} ;
} ) ;
} La colección y el producto modificado reciben referencias nuevas. Los demás productos conservan las suyas.
Pensar que const vuelve inmutable un objeto.
Mutar un parámetro sin que el nombre o contrato lo comuniquen.
Crear una copia exterior y mutar después una referencia anidada compartida.
Utilizar sort esperando un array nuevo.
Copiar cada valor del programa por dogma.
Confundir mutación local controlada con estado global compartido.
Esperar que Object.freeze sea profundo.
Mutar estado de React y reutilizar la misma referencia.
Llamar “inmutable” a un código que todavía cambia ramas anidadas.
Ignorar el costo de copiar estructuras grandes.
Los primitivos son inmutables; los objetos son mutables por defecto.
const evita reasignación, no mutación.
Varias referencias pueden observar el mismo cambio.
La mutación local puede ser segura y clara.
La mutación de datos compartidos necesita un contrato explícito.
Una actualización inmutable crea identidades nuevas en las ramas modificadas.
Las ramas sin cambios pueden compartirse.
La identidad es importante para UI, cachés e historial.
Inmutabilidad es una herramienta de diseño, no una regla absoluta.
¿Qué imprime el último console.log?
JavaScript
Copiar const user = {
profile : {
city : "Bogotá" ,
} ,
} ;
const copy = {
... user,
} ;
copy. profile. city = "Cali" ;
console. log ( user. profile. city) ; Respuesta Imprime "Cali". La copia exterior es nueva, pero profile continúa siendo el mismo objeto compartido.
Copias superficiales y profundas explica exactamente qué se duplica en cada estrategia y cuándo structuredClone es apropiado.