Como sabemos por el capítulo Tipos de datos, hay ocho tipos de datos en JavaScript. Siete de ellos se llaman "primitivos", porque sus valores contienen sólo una cosa (ya sea una cadena, un número o lo que sea).
Por el contrario, los objetos se utilizan para almacenar colecciones con clave de diversos datos y entidades más complejas. En JavaScript, los objetos penetran en casi todos los aspectos del lenguaje. Por eso debemos entenderlos primero antes de profundizar en ningún otro lugar.
Se puede crear un objeto con corchetes {…}
con una lista opcional de propiedades . Una propiedad es un par “clave: valor”, donde key
es una cadena (también llamada “nombre de propiedad”) y value
puede ser cualquier cosa.
Podemos imaginar un objeto como un armario con expedientes firmados. Cada dato se almacena en su archivo mediante la clave. Es fácil encontrar un archivo por su nombre o agregar/eliminar un archivo.
Se puede crear un objeto vacío (“gabinete vacío”) usando una de dos sintaxis:
dejar usuario = nuevo Objeto(); // sintaxis del "constructor de objetos" dejar usuario = {}; // sintaxis del "objeto literal"
Normalmente se utilizan los corchetes {...}
. Esa declaración se llama objeto literal .
Inmediatamente podemos poner algunas propiedades en {...}
como pares “clave: valor”:
let usuario = { // un objeto nombre: "John", // por clave "nombre" valor de tienda "John" edad: 30 // por clave "edad" valor de tienda 30 };
Una propiedad tiene una clave (también conocida como "nombre" o "identificador") antes de los dos puntos ":"
y un valor a la derecha de ella.
En el objeto user
, hay dos propiedades:
La primera propiedad tiene el nombre "name"
y el valor "John"
.
El segundo tiene el nombre "age"
y el valor 30
.
El objeto user
resultante se puede imaginar como un gabinete con dos archivos firmados etiquetados como "nombre" y "edad".
Podemos agregar, eliminar y leer archivos en cualquier momento.
Se puede acceder a los valores de propiedad mediante la notación de puntos:
// obtener valores de propiedad del objeto: alerta (nombre.usuario); // John alerta (usuario.edad); // 30
El valor puede ser de cualquier tipo. Agreguemos uno booleano:
usuario.isAdmin = verdadero;
Para eliminar una propiedad, podemos usar el operador delete
:
eliminar usuario.edad;
También podemos usar nombres de propiedades de varias palabras, pero luego deben ir entre comillas:
dejar usuario = { nombre: "Juan", edad: 30, "le gustan los pájaros": verdadero // el nombre de la propiedad de varias palabras debe estar entre comillas };
La última propiedad de la lista puede terminar con una coma:
dejar usuario = { nombre: "Juan", edad: 30, }
Esto se llama coma "final" o "colgante". Hace que sea más fácil agregar/eliminar/mover propiedades, porque todas las líneas se vuelven iguales.
Para propiedades de varias palabras, el acceso por punto no funciona:
// esto daría un error de sintaxis usuario.le gustan los pájaros = verdadero
JavaScript no entiende eso. Piensa que nos dirigimos a user.likes
y luego da un error de sintaxis cuando se encuentra con birds
inesperados.
El punto requiere que la clave sea un identificador de variable válido. Eso implica: no contiene espacios, no comienza con un dígito y no incluye caracteres especiales (se permiten $
y _
).
Existe una “notación entre corchetes” alternativa que funciona con cualquier cadena:
dejar usuario = {}; // colocar usuario["le gustan los pájaros"] = verdadero; // conseguir alert(usuario["le gustan los pájaros"]); // verdadero // borrar eliminar usuario["le gustan los pájaros"];
Ahora todo está bien. Tenga en cuenta que la cadena dentro de los corchetes está entre comillas correctamente (cualquier tipo de comillas servirá).
Los corchetes también proporcionan una forma de obtener el nombre de la propiedad como resultado de cualquier expresión, a diferencia de una cadena literal, como a partir de una variable, como se muestra a continuación:
let key = "le gustan los pájaros"; // igual que el usuario["le gustan los pájaros"] = verdadero; usuario[clave] = verdadero;
Aquí, la key
variable puede calcularse en tiempo de ejecución o depender de la entrada del usuario. Y luego lo usamos para acceder a la propiedad. Eso nos da una gran flexibilidad.
Por ejemplo:
dejar usuario = { nombre: "Juan", edad: 30 }; let key = Prompt("¿Qué desea saber sobre el usuario?", "nombre"); // acceso por variable alerta (usuario [clave]); // John (si ingresa "nombre")
La notación de puntos no se puede utilizar de forma similar:
dejar usuario = { nombre: "Juan", edad: 30 }; let clave = "nombre"; alerta (usuario.clave) // indefinido
Podemos usar corchetes en un objeto literal al crear un objeto. Eso se llama propiedades calculadas .
Por ejemplo:
let fruit = Prompt("¿Qué fruta comprar?", "manzana"); dejar bolsa = { [fruta]: 5, // el nombre de la propiedad se toma de la variable fruta }; alerta( bolsa.manzana ); // 5 si fruta="manzana"
El significado de una propiedad calculada es simple: [fruit]
significa que el nombre de la propiedad debe tomarse de fruit
.
Entonces, si un visitante ingresa "apple"
, bag
se convertirá en {apple: 5}
.
Básicamente, eso funciona igual que:
let fruit = Prompt("¿Qué fruta comprar?", "manzana"); dejar bolsa = {}; // toma el nombre de la propiedad de la variable de fruta bolsa[fruta] = 5;
…Pero se ve mejor.
Podemos usar expresiones más complejas entre corchetes:
let fruta = 'manzana'; dejar bolsa = { [fruta + 'Computadoras']: 5 // bolsa.appleComputers = 5 };
Los corchetes son mucho más poderosos que la notación de puntos. Permiten cualquier nombre de propiedad y variable. Pero también son más complicados de escribir.
Entonces, la mayoría de las veces, cuando los nombres de las propiedades son conocidos y simples, se usa el punto. Y si necesitamos algo más complejo, cambiamos a corchetes.
En el código real, a menudo utilizamos variables existentes como valores para los nombres de propiedades.
Por ejemplo:
función makeUser(nombre, edad) { devolver { nombre: nombre, edad: edad, // ...otras propiedades }; } let usuario = makeUser("Juan", 30); alerta(nombre.usuario); // John
En el ejemplo anterior, las propiedades tienen los mismos nombres que las variables. El caso de uso de crear una propiedad a partir de una variable es tan común que existe una abreviatura de valor de propiedad especial para acortarlo.
En lugar de name:name
podemos simplemente escribir name
, así:
función makeUser(nombre, edad) { devolver { nombre, // igual que nombre: nombre edad, // igual que edad: edad //... }; }
Podemos usar tanto propiedades normales como abreviaturas en el mismo objeto:
dejar usuario = { nombre, // igual que nombre:nombre edad: 30 };
Como ya sabemos, una variable no puede tener un nombre igual a una de las palabras reservadas del idioma como "for", "let", "return", etc.
Pero para una propiedad de objeto, no existe tal restricción:
// estas propiedades están bien dejar objeto = { para: 1, sea: 2, retorno: 3 }; alerta (obj.for + obj.let + obj.return); // 6
En resumen, no existen limitaciones en cuanto a los nombres de las propiedades. Pueden ser cualquier cadena o símbolo (un tipo especial para identificadores, que se tratará más adelante).
Otros tipos se convierten automáticamente en cadenas.
Por ejemplo, un número 0
se convierte en una cadena "0"
cuando se usa como clave de propiedad:
dejar objeto = { 0: "prueba" // igual que "0": "prueba" }; // ambas alertas acceden a la misma propiedad (el número 0 se convierte en cadena "0") alerta(obj["0"]); // prueba alerta(obj[0]); // prueba (misma propiedad)
Hay un problema menor con una propiedad especial llamada __proto__
. No podemos establecerlo en un valor que no sea de objeto:
dejar objeto = {}; obj.__proto__ = 5; // asigna un número alerta(obj.__proto__); // [objeto Objeto] - el valor es un objeto, no funcionó según lo previsto
Como vemos en el código, se ignora la asignación a una primitiva 5
.
Cubriremos la naturaleza especial de __proto__
en capítulos posteriores y sugeriremos formas de solucionar dicho comportamiento.
Una característica notable de los objetos en JavaScript, en comparación con muchos otros lenguajes, es que es posible acceder a cualquier propiedad. ¡No habrá ningún error si la propiedad no existe!
Leer una propiedad inexistente simplemente devuelve undefined
. Entonces podemos probar fácilmente si la propiedad existe:
dejar usuario = {}; alerta (usuario.noSuchProperty === indefinido); // verdadero significa "no existe tal propiedad"
También hay un operador especial "in"
para eso.
La sintaxis es:
"clave" en el objeto
Por ejemplo:
let usuario = { nombre: "John", edad: 30 }; alerta ("edad" en el usuario); // verdadero, la edad del usuario existe alerta( "blabla" en usuario ); // falso, usuario.blabla no existe
Tenga en cuenta que en el in
izquierdo debe haber un nombre de propiedad . Suele ser una cadena entre comillas.
Si omitimos las comillas, eso significa que una variable debe contener el nombre real que se va a probar. Por ejemplo:
dejar usuario = {edad: 30}; let clave = "edad"; alerta (teclee usuario); // verdadero, la propiedad "edad" existe
¿Por qué existe el operador in
? ¿No es suficiente compararlo con undefined
?
Bueno, la mayoría de las veces la comparación con undefined
funciona bien. Pero hay un caso especial en el que falla, pero "in"
funciona correctamente.
Es cuando existe una propiedad de objeto, pero se almacena undefined
:
dejar objeto = { prueba: indefinida }; alerta (obj.prueba); // no está definido, entonces, ¿no existe tal propiedad? alerta( "prueba" en obj ); // verdadero, ¡la propiedad existe!
En el código anterior, la propiedad obj.test
técnicamente existe. Entonces el operador in
funciona bien.
Situaciones como esta ocurren muy raramente, porque undefined
no debe asignarse explícitamente. Principalmente usamos null
para valores "desconocidos" o "vacíos". Entonces el operador in
es un invitado exótico en el código.
Para recorrer todas las claves de un objeto, existe una forma especial de bucle: for..in
. Esto es completamente diferente del constructo for(;;)
que estudiamos antes.
La sintaxis:
para (ingrese el objeto) { // ejecuta el cuerpo de cada clave entre las propiedades del objeto }
Por ejemplo, generemos todas las propiedades del user
:
dejar usuario = { nombre: "Juan", edad: 30, isAdmin: verdadero }; para (dejar ingresar usuario) { // llaves alerta( clave ); // nombre, edad, esAdmin // valores para las claves alerta (usuario [clave]); // Juan, 30 años, cierto }
Tenga en cuenta que todas las construcciones "for" nos permiten declarar la variable de bucle dentro del bucle, como let key
aquí.
Además, aquí podríamos usar otro nombre de variable en lugar de key
. Por ejemplo, "for (let prop in obj)"
también se usa ampliamente.
¿Están ordenados los objetos? En otras palabras, si recorremos un objeto, ¿obtenemos todas las propiedades en el mismo orden en que se agregaron? ¿Podemos confiar en esto?
La respuesta corta es: "ordenadas de una manera especial": las propiedades de los enteros están ordenadas, otras aparecen en orden de creación. Los detalles siguen.
Como ejemplo, consideremos un objeto con códigos telefónicos:
dejar códigos = { "49": "Alemania", "41": "Suiza", "44": "Gran Bretaña", // .., "1": "Estados Unidos" }; para (dejar código en códigos) { alerta(código); // 1, 41, 44, 49 }
El objeto puede usarse para sugerir una lista de opciones al usuario. Si estamos creando un sitio principalmente para una audiencia alemana entonces probablemente queramos que 49
sea el primero.
Pero si ejecutamos el código, vemos una imagen totalmente diferente:
Estados Unidos (1) va primero
luego Suiza (41) y así sucesivamente.
Los códigos telefónicos van en orden ascendente, porque son números enteros. Entonces vemos 1, 41, 44, 49
.
¿Propiedades enteras? ¿Qué es eso?
El término "propiedad de número entero" aquí significa una cadena que se puede convertir hacia y desde un número entero sin realizar cambios.
Entonces, "49"
es un nombre de propiedad entero, porque cuando se transforma a un número entero y viceversa, sigue siendo el mismo. Pero "+49"
y "1.2"
no lo son:
// Número(...) se convierte explícitamente en un número // Math.trunc es una función incorporada que elimina la parte decimal alerta (Cadena (Math.trunc (Número ("49")))); // "49", igual, propiedad entera alerta (Cadena (Math.trunc (Número ("+49")))); // "49", no es el mismo "+49" ⇒ propiedad no entera alerta (Cadena (Math.trunc (Número ("1.2")))); // "1", no es el mismo "1.2" ⇒ propiedad no entera
…Por otro lado, si las claves no son enteras, entonces se enumeran en el orden de creación, por ejemplo:
dejar usuario = { nombre: "Juan", apellido: "Smith" }; edad.usuario = 25; // agrega uno más // las propiedades no enteras se enumeran en el orden de creación para (dejar prop en usuario) { alerta( prop ); // nombre, apellido, edad }
Entonces, para solucionar el problema con los códigos telefónicos, podemos "hacer trampa" haciendo que los códigos no sean números enteros. Agregar un signo más "+"
antes de cada código es suficiente.
Como esto:
dejar códigos = { "+49": "Alemania", "+41": "Suiza", "+44": "Gran Bretaña", // .., "+1": "Estados Unidos" }; para (dejar código en códigos) { alerta( +código ); // 49, 41, 44, 1 }
Ahora funciona según lo previsto.
Los objetos son matrices asociativas con varias características especiales.
Almacenan propiedades (pares clave-valor), donde:
Las claves de propiedad deben ser cadenas o símbolos (normalmente cadenas).
Los valores pueden ser de cualquier tipo.
Para acceder a una propiedad, podemos utilizar:
La notación de puntos: obj.property
.
Notación entre corchetes obj["property"]
. Los corchetes permiten tomar la clave de una variable, como obj[varWithKey]
.
Operadores adicionales:
Para eliminar una propiedad: delete obj.prop
.
Para comprobar si existe una propiedad con la clave dada: "key" in obj
.
Para iterar sobre un objeto: bucle for (let key in obj)
.
Lo que hemos estudiado en este capítulo se llama "objeto simple", o simplemente Object
.
Hay muchos otros tipos de objetos en JavaScript:
Array
para almacenar colecciones de datos ordenadas,
Date
para almacenar la información sobre la fecha y la hora,
Error
para almacenar la información sobre un error.
…Etcétera.
Tienen sus características especiales que estudiaremos más adelante. A veces la gente dice algo como "tipo de matriz" o "tipo de fecha", pero formalmente no son tipos propios, sino que pertenecen a un único tipo de datos de "objeto". Y lo extienden de diversas formas.
Los objetos en JavaScript son muy poderosos. Aquí acabamos de tocar la superficie de un tema que es realmente enorme. Trabajaremos estrechamente con objetos y aprenderemos más sobre ellos en partes posteriores del tutorial.
importancia: 5
Escribe el código, una línea para cada acción:
Crea un user
de objeto vacío.
Agregue el name
de la propiedad con el valor John
.
Agregue el surname
de la propiedad con el valor Smith
.
Cambie el valor del name
a Pete
.
Elimine el name
de la propiedad del objeto.
dejar usuario = {}; nombre.usuario = "Juan"; usuario.apellido = "Smith"; nombre.usuario = "Pete"; eliminar nombre de usuario;
importancia: 5
Escriba la función isEmpty(obj)
que devuelve true
si el objeto no tiene propiedades, false
en caso contrario.
Debería funcionar así:
dejar programar = {}; alerta (está vacío (horario)); // verdadero horario["8:30"] = "levantarse"; alerta (está vacío (horario)); // FALSO
Abra una caja de arena con pruebas.
Simplemente recorra el objeto y return false
inmediatamente si hay al menos una propiedad.
la función está vacía (obj) { para (dejar ingresar obj) { // si el ciclo ha comenzado, hay una propiedad devolver falso; } devolver verdadero; }
Abra la solución con pruebas en un sandbox.
importancia: 5
Disponemos de un objeto que almacena los salarios de nuestro equipo:
dejar salarios = { Juan: 100, Ana: 160, Pete: 130 }
Escriba el código para sumar todos los salarios y guárdelo en la variable sum
. Debería ser 390
en el ejemplo anterior.
Si salaries
están vacíos, entonces el resultado debe ser 0
.
dejar salarios = { Juan: 100, Ana: 160, Pete: 130 }; sea suma = 0; for (deje ingresar los salarios) { suma += salarios[clave]; } alerta(suma); // 390
importancia: 3
Cree una función multiplyNumeric(obj)
que multiplique todos los valores de propiedad numérica de obj
por 2
.
Por ejemplo:
// antes de la llamada dejar menú = { ancho: 200, altura: 300, título: "Mi menú" }; multiplicarNumérico(menú); // después de la llamada menú = { ancho: 400, altura: 600, título: "Mi menú" };
Tenga en cuenta que multiplyNumeric
no necesita devolver nada. Debería modificar el objeto en su lugar.
PD: Utilice typeof
para buscar un número aquí.
Abra una caja de arena con pruebas.
función multiplicarNumeric(obj) { para (dejar ingresar obj) { if (tipo de obj[clave] == 'número') { obj[clave] *= 2; } } }
Abra la solución con pruebas en un sandbox.