Ir al contenido
angel.library

Objetos, propiedades y copias

Un objeto agrupa propiedades y se comparte por referencia. Usa punto para una clave conocida y corchetes para una clave calculada. Object.keys, values y entries solo incluyen propiedades propias enumerables con clave string; Reflect.ownKeys también incluye symbols y propiedades no enumerables.

Un objeto agrupa propiedades formadas por una clave y un valor. Las claves son strings o symbols; los valores pueden ser de cualquier tipo. Los objetos son mutables y se asignan por referencia.

const user = {
id: 7,
name: "Sofía",
active: true
}
const sameUser = user
sameUser.active = false
user.active // false
sameUser === user // true

La notación de punto se usa con claves conocidas. Los corchetes aceptan una expresión y son necesarios para claves dinámicas, con espacios o guardadas en otra variable.

const field = "name"
const user = { name: "Sofía", "account-id": 42 }
user.name // 'Sofía'
user[field] // 'Sofía'
user["account-id"] // 42
user.role = "editor"
user["lastLogin"] = "2026-08-18"
delete user.role
user.role // undefined

object[variable] busca la propiedad cuyo nombre contiene la variable. object.variable busca literalmente una propiedad llamada variable.

OperaciónDevuelveRevisa heredadasCaso de uso
Object.hasOwn(object, key)booleanonovalidar un campo propio
key in objectbooleanosícomprobar una capacidad en toda la cadena de prototipos
object[key] !== undefinedbooleanono distingue ausencia de valor undefinedsolo si esa ambigüedad es aceptable
const settings = { theme: undefined }
Object.hasOwn(settings, "theme") // true
Object.hasOwn(settings, "lang") // false
"toString" in settings // true: viene del prototipo
settings.theme !== undefined // false: no detecta que la clave existe
MétodoDevuelveMutaCaso de uso
Object.keys(object)array de claves propias enumerablesnorecorrer nombres de campos
Object.values(object)array de valores propios enumerablesnosumar o comprobar valores
Object.entries(object)array de pares [key, value]nofiltrar o mapear un objeto
Object.fromEntries(entries)objeto nuevonoreconstruir desde pares o Map
Object.assign(target, ...sources)el objeto targetsí, el targetmezclar propiedades superficialmente
const product = { name: "Mouse", price: 30, stock: 4 }
Object.keys(product)
// ['name', 'price', 'stock']
Object.values(product)
// ['Mouse', 30, 4]
Object.entries(product)
// [['name', 'Mouse'], ['price', 30], ['stock', 4]]
const publicData = Object.fromEntries(
Object.entries(product).filter(([key]) => key !== "stock")
)
publicData // { name: 'Mouse', price: 30 }
product // { name: 'Mouse', price: 30, stock: 4 }
APIStringSymbolNo enumerableHeredada
Object.keys / values / entriessínonono
Object.getOwnPropertyNamessínosíno
Object.getOwnPropertySymbolsnosísíno
Reflect.ownKeyssísísíno
for...insínonosí
const internal = Symbol("internal")
const model = { visible: true, [internal]: 42 }
Object.defineProperty(model, "id", { value: 7, enumerable: false })
Object.keys(model) // ['visible']
Object.getOwnPropertyNames(model) // ['visible', 'id']
Object.getOwnPropertySymbols(model) // [Symbol(internal)]
Reflect.ownKeys(model) // ['visible', 'id', Symbol(internal)]
const scores = { ana: 8, leo: 6 }
const doubled = Object.fromEntries(
Object.entries(scores).map(([name, score]) => [name, score * 2])
)
doubled // { ana: 16, leo: 12 }
scores // { ana: 8, leo: 6 }

Ambos recorren un iterable y llaman un callback. Object.groupBy crea un objeto sin prototype con claves string o symbol; Map.groupBy conserva claves de cualquier tipo.

const products = [
{ name: "Mouse", category: "hardware" },
{ name: "Editor", category: "software" },
{ name: "Monitor", category: "hardware" }
]
const byCategory = Object.groupBy(products, (product) => product.category)
byCategory.hardware
// [
// { name: 'Mouse', category: 'hardware' },
// { name: 'Monitor', category: 'hardware' }
// ]

Los arrays dentro del resultado contienen las mismas referencias que la entrada; agrupar no clona los elementos. Verifica compatibilidad en runtimes antiguos.

Como el resultado de Object.groupBy tiene prototype null, no llames byCategory.hasOwnProperty(...); usa Object.hasOwn(byCategory, key).

const response = {
id: 15,
profile: { displayName: "Noa" },
role: "admin"
}
const {
id,
profile: { displayName: name },
role = "user"
} = response
id // 15
name // 'Noa'
role // 'admin'
const property = "status"
const record = {
[property]: "ready",
[`${property}At`]: "2026-08-18"
}
record // { status: 'ready', statusAt: '2026-08-18' }

El spread y Object.assign copian solo el primer nivel. Los objetos anidados continúan compartiendo referencia.

const original = {
name: "Panel",
preferences: { compact: false }
}
const shallowCopy = { ...original }
shallowCopy.name = "Dashboard"
shallowCopy.preferences.compact = true
original.name // 'Panel'
original.preferences.compact // true: el objeto interno se comparte

Para datos compatibles con el algoritmo de clonación estructurada, usa structuredClone. Conserva ciclos, Map, Set, Date, typed arrays y otros tipos; no clona funciones ni nodos del DOM.

const deepCopy = structuredClone(original)
deepCopy.preferences.compact = false
deepCopy.preferences.compact // false
original.preferences.compact // true

JSON.parse(JSON.stringify(value)) no es un clonador general: pierde undefined, symbols y métodos; convierte Date en texto y falla con ciclos.

MétodoAñadirEliminarCambiar valoresProfundidad
Object.preventExtensions()nosísísuperficial
Object.seal()nonosísuperficial
Object.freeze()nononosuperficial

Todos mutan el estado interno del objeto recibido y devuelven ese mismo objeto.

const config = Object.freeze({
api: "/v1",
options: { retries: 2 }
})
Object.isFrozen(config) // true
config.options.retries = 3
config.options.retries // 3: freeze no es profundo

En módulos ES y modo estricto, intentar cambiar una propiedad congelada puede lanzar TypeError. Para inmutabilidad profunda necesitas congelar recursivamente o adoptar estructuras y patrones que no expongan mutación.

=== compara referencias entre objetos. Object.is se parece a ===, pero distingue 0 de -0 y considera que NaN es igual a sí mismo.

;({}) === {} // false
const item = {}
item === item // true
Object.is(NaN, NaN) // true
NaN === NaN // false
Object.is(0, -0) // false
0 === -0 // true

Para igualdad estructural, define qué campos importan o usa una solución especializada. Serializar con JSON para comparar es frágil por el orden de claves y los tipos no representables.

Cada propiedad tiene indicadores como writable, enumerable y configurable. Object.defineProperty permite controlarlos.

const account = {}
Object.defineProperty(account, "id", {
value: 42,
writable: false,
enumerable: true,
configurable: false
})
Object.getOwnPropertyDescriptor(account, "id")
// {
// value: 42,
// writable: false,
// enumerable: true,
// configurable: false
// }

Los getters calculan un valor al leer y los setters interceptan una asignación. Evita ocultar trabajo costoso o efectos inesperados detrás de una lectura aparentemente simple.

const rectangle = {
width: 4,
height: 3,
get area() {
return this.width * this.height
}
}
rectangle.area // 12
function updateProfile(user, changes) {
return {
...user,
profile: {
...user.profile,
...changes
}
}
}
const user = {
id: 1,
profile: { name: "Eva", city: "Cali" }
}
const updated = updateProfile(user, { city: "Bogotá" })
updated
// { id: 1, profile: { name: 'Eva', city: 'Bogotá' } }
user.profile.city // 'Cali'
updated !== user // true